Accessibility on android custom view not working - Stack Overflow

I have a custom view , which extends ViewGroup, i.e CustomViewGrouup. There I am adding some ImageView.

I have a custom view , which extends ViewGroup, i.e CustomViewGrouup. There I am adding some ImageView. When user will click on some specific rectangular area on the CustomViewGroup, then I need to behave it like a button, i.e Announced by Talkback , like "Rectangle 1, Double Tap to activate". Then, if user double taps on that button I need to perform the code for it.

I tried to add some virtual view structure, with getAccessibilityNodeProvider. Now, when I tap on some defined rectangular area, then it annouces "Rectangle 1" not like "...Double Tap to activate. When I double click on that rectangular area, it doesn't perform desired action in the "performAction" method.

Code is like below :

package com.example.myapplication;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
import android.widget.Button;
import android.widget.ImageView;

import androidx.annotation.Nullable;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;

import java.util.ArrayList;
import java.util.List;

public class MyCustomViewGroup extends ViewGroup {

    private ImageView imageView;

    // Logical rectangles to represent the newspaper article sections
    private List<Rect> logicalRects = new ArrayList<>();
    private int focusedRectIndex = -1;
    private int previousFocusedRectIndex = -1;
    private AccessibilityNodeProvider nodeProvider;
    private Paint rectPaint;

    boolean drawRects = false;


    public MyCustomViewGroup(Context context) {
        super(context);
        init(context);
    }

    public MyCustomViewGroup(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public MyCustomViewGroup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        
        imageView = new ImageView(context);
       
        imageView.setImageResource(R.drawable.sample_paper_image);

        imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);

        imageView.setBackgroundColor(Color.LTGRAY);

        imageView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
        imageView.setClickable(false);
        imageView.setFocusable(false);

        addView(imageView);

        initLogicalRects();
    }


    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);

        if (drawRects) {
            drawRectangles(canvas);
        }
    }

    private void drawRectangles(Canvas canvas) {
        // Draw the logical rectangles
        for (int i = 0; i < logicalRects.size(); i++) {
            if (i == focusedRectIndex) {
                // Highlight the focused rectangle
                rectPaint.setColor(Color.GREEN);
            } else {
                // Default color for non-focused rectangles
                rectPaint.setColor(Color.RED);
            }
            canvas.drawRect(logicalRects.get(i), rectPaint);
        }
    }

    private void initLogicalRects() {
        // Define some example rectangles
        logicalRects.add(new Rect(50, 50, 200, 150)); // Rectangle 1
        logicalRects.add(new Rect(250, 50, 400, 150)); // Rectangle 2
        logicalRects.add(new Rect(50, 200, 200, 300)); // Rectangle 3
        logicalRects.add(new Rect(400, 400, 800, 800)); // Rectangle 4

        if (drawRects) {
            rectPaint = new Paint();
            rectPaint.setStyle(Paint.Style.STROKE);
            rectPaint.setStrokeWidth(8);

            // Allow ViewGroup to draw
            setWillNotDraw(false);

            invalidate();
        }

    }

    @Override
    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
        if (nodeProvider == null) {
            nodeProvider = new CustomAccessibilityNodeProvider();
        }
        return nodeProvider;
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // Measure the ImageView to fill the parent
        measureChild(imageView, widthMeasureSpec, heightMeasureSpec);

        // Set the measured dimensions of the ViewGroup
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(width, height);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // Position the ImageView to fill the entire ViewGroup
        imageView.layout(0, 0, r - l, b - t);
    }

    private void performClickAction(int rectIndex) {
        // Handle the action for the rectangle
        Log.d("MyCustomViewGroup", "Rectangle " + (rectIndex + 1) + " clicked!");
    }

    @Override
    public boolean onHoverEvent(MotionEvent event) {
        AccessibilityManager accessibilityManager =
                (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
        if (accessibilityManager != null && accessibilityManager.isEnabled()) {


            // Forward hover events to accessibility
            int index = getRectIndexAt(event.getX(), event.getY());
            if (index != -1) {
                if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
                    nodeProvider.performAction(index, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
                } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
                    nodeProvider.performAction(index, AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
                }
            }
            Log.d("MyCustomViewGroup", "Hover event: Action = " + event.getAction() + ", Index = " + index);
            return true;
        }
        return super.onHoverEvent(event);
    }


    private int getRectIndexAt(float x, float y) {
        for (int i = 0; i < logicalRects.size(); i++) {
            if (logicalRects.get(i).contains((int) x, (int) y)) {
                return i;
            }
        }
        return -1;
    }


    private class CustomAccessibilityNodeProvider extends AccessibilityNodeProvider {
        @Override
        public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
            if (virtualViewId == View.NO_ID) {
                // Describe the MyCustomViewGroup itself
                AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(MyCustomViewGroup.this);
                onInitializeAccessibilityNodeInfo(info);

                // Add virtual nodes as children
                for (int i = 0; i < logicalRects.size(); i++) {
                    info.addChild(MyCustomViewGroup.this, i);
                }

                info.setClassName(ViewGroup.class.getName());

                // Prevent the ViewGroup itself from being focusable
                info.setFocusable(false);
                info.setClickable(false);
                //info.setImportantForAccessibility(false);
                return info;
            } else {
                // Describe a virtual rectangle
                return createVirtualAccessibilityNodeInfo(virtualViewId);
            }
        }

        @Override
        public boolean performAction(int virtualViewId, int action, Bundle arguments) {
            Log.d("MyCustomViewGroup", "Perform action: Action = " + action + ", Index = " + virtualViewId);
            if (virtualViewId == View.NO_ID) {
                return super.performAction(virtualViewId, action, arguments);
            }

            if (virtualViewId >= 0 && virtualViewId < logicalRects.size()) {
                if (action == AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS) {
                    focusedRectIndex = virtualViewId;
                    //sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
                    sendAccessibilityEventForVirtualNode(virtualViewId, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
                    if (drawRects) invalidate();
                    return true;
                } else if (action == AccessibilityNodeInfo.ACTION_CLICK) {
                    // Perform the action
                    performClickAction(virtualViewId);

                    // Send an event to announce the click
                    sendAccessibilityEventForVirtualNode(virtualViewId, AccessibilityEvent.TYPE_VIEW_CLICKED);
                    Log.d("MyCustomViewGroup", "Rectangle " + virtualViewId + " clicked via double-tap!");
                    return true;
                }
            }
            return false;
        }


        private AccessibilityNodeInfo createVirtualAccessibilityNodeInfo(int index) {
            if (index < 0 || index >= logicalRects.size()) {
                return null;
            }

            AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
            info.setPackageName(getContext().getPackageName());
            info.setClassName(Button.class.getName());
            info.setSource(MyCustomViewGroup.this, index);

            // Describe the logical rectangle
            Rect rect = logicalRects.get(index);
            info.setBoundsInParent(rect);
            info.setContentDescription("Rectangle " + (index + 1));
            info.setEnabled(true);
            info.setFocusable(true);
            info.setClickable(true);
            
            // Add focus and click actions using AccessibilityAction
            info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS);
            info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
            
            return info;
        }
    }

    private void sendAccessibilityEventForVirtualNode(int virtualViewId, int eventType) {
        AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
        event.setPackageName(getContext().getPackageName());
        event.setClassName(Button.class.getName());
        event.setContentDescription("Rectangle " + (virtualViewId + 1)); // Description of the rectangle
        event.setSource(this, virtualViewId);
        Log.d("MyCustomViewGroup", "Accessibility Event: Type = " + eventType + ", Index = " + virtualViewId);
        getParent().requestSendAccessibilityEvent(MyCustomViewGroup.this, event);
    }

}

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745273471a4619892.html

相关推荐

  • Accessibility on android custom view not working - Stack Overflow

    I have a custom view , which extends ViewGroup, i.e CustomViewGrouup. There I am adding some ImageView.

    1小时前
    20

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信