日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

视图添加字段_使用ExploreByTouchHelper辅助类为自定义视图添加虚拟视图

發(fā)布時(shí)間:2023/12/10 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 视图添加字段_使用ExploreByTouchHelper辅助类为自定义视图添加虚拟视图 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在安卓開(kāi)發(fā)過(guò)程中,為了視覺(jué)和功能的需要開(kāi)發(fā)者經(jīng)常會(huì)使用自定義視圖

大多數(shù)的自定義視圖是組合現(xiàn)有的控件來(lái)完成特定的功能

但是,有一種自定義視圖是通過(guò)畫筆在畫布上畫出自定義的子視圖的,例如日期控件,顏色選擇面板等

由于自定義視圖的子視圖是用畫筆繪制的,所以這些子視圖無(wú)法被無(wú)障礙服務(wù)訪問(wèn)

為了解決此種問(wèn)題,Android系統(tǒng)在API16引入虛擬視圖概念

開(kāi)發(fā)人員可以通過(guò)虛擬視圖模擬出視圖結(jié)構(gòu),從而讓無(wú)障礙服務(wù)能夠訪問(wèn)這些繪制的子視圖

今天就來(lái)講講使用支持庫(kù)中的ExploreByTouchHelper工具類實(shí)現(xiàn)虛擬視圖的方法:

1. 為自定義視圖添加無(wú)障礙代理

在自定義視圖初始化時(shí),調(diào)用setAccessibilityDelegate()方法設(shè)置無(wú)障礙代理

參數(shù)是實(shí)現(xiàn)了ExploreByTouchHelper工具類的對(duì)象

如果想支持API更早的版本可以調(diào)用ViewCompat的setAccessibilityDelegate()方法,如下所示:

mTouchHelper = new CustomViewTouchHelper(this);ViewCompat.setAccessibilityDelegate(this, mTouchHelper);

2. 實(shí)現(xiàn)無(wú)障礙代理

通過(guò)繼承ExploreByTouchHelper工具類實(shí)現(xiàn)無(wú)障礙代理,如下所示:

private class CustomViewTouchHelper extends ExploreByTouchHelper { public CustomViewTouchHelper(VirtualSubview view) { super(view); }...}

3. 為虛擬視圖添加子視圖節(jié)點(diǎn)

通過(guò)重寫getVisibleVirtualViews()方法確定虛擬視圖中有多少子節(jié)點(diǎn)

添加的子節(jié)點(diǎn)就是無(wú)障礙服務(wù)訪問(wèn)時(shí)能訪問(wèn)到的無(wú)障礙焦點(diǎn)

代碼樣例如下:

@Override protected void getVisibleVirtualViews(List<Integer> virtualViewIds) { final List<VirtualView> childs = mChildren; final int count = childs.size(); for (int i = 0; i < count; i++) { VirtualView child = childs.get(i); virtualViewIds.add(child.mId); }}

4. 為虛擬視圖節(jié)點(diǎn)添加ID

當(dāng)用戶開(kāi)啟無(wú)障礙服務(wù)訪問(wèn)虛擬節(jié)點(diǎn)時(shí),會(huì)調(diào)用getVirtualViewAt()方法確定用戶觸摸的區(qū)域?qū)儆谀囊粋€(gè)子視圖

我們需要在此方法中通過(guò)x和y坐標(biāo)計(jì)算出當(dāng)前操作的虛擬節(jié)點(diǎn)的id

代碼如下所示:

@Override protected int getVirtualViewAt(float x, float y) { VirtualView view = findVirtualViewByBoords(x, y); if (view == null) return INVALID_ID; //返回?zé)o效的節(jié)點(diǎn)return view.mId; }

5. 為虛擬視圖填充必要的無(wú)障礙屬性

為了讓無(wú)障礙服務(wù)正確地反饋虛擬視圖的相關(guān)信息,我們需要為虛擬視圖填充必要的無(wú)障礙屬性信息

下面是填充信息的方法:

//在此方法中設(shè)置虛擬視圖的無(wú)障礙事件信息 @Override protected void onPopulateEventForVirtualView( int virtualViewId,AccessibilityEvent event) { //調(diào)用此方法給無(wú)障礙事件填充text字段,text字段會(huì)被TalkBack朗讀出來(lái)VirtualView item = findVirtualViewById(virtualViewId); if (item != null)event.getText().add(item.mText); }//調(diào)用此方法填充子虛擬視圖的無(wú)障礙nodeinfo的屬性 @Override protected void onPopulateNodeForVirtualView(int virtualViewId,AccessibilityNodeInfoCompat node) { //調(diào)用此方法在NodeInfo中設(shè)置子虛擬視圖的text字段,此字段會(huì)被talkback朗讀出來(lái)VirtualView item = findVirtualViewById(virtualViewId); if (item == null) return;node.setText(item.mText); Rect bounds = item.mBounds; //調(diào)用此方法設(shè)置子虛擬視圖的焦點(diǎn)大小,焦點(diǎn)大小與實(shí)際畫的視圖一致大小。此方法必須調(diào)用。node.setBoundsInParent(bounds);//調(diào)用此方法設(shè)置nodeinfo都能處理哪些無(wú)障礙事件。調(diào)用此方法只能說(shuō)明nodeinfo能處理這些action,不是觸發(fā)action,也不是具體處理action。這里可以設(shè)置多個(gè)action。node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK); //調(diào)用此方法代表此nodeinfo節(jié)點(diǎn)是可以被選中。當(dāng)設(shè)置為true代表可以被選中,如復(fù)選框就需要設(shè)置為true。node.setCheckable(true); //設(shè)置無(wú)障礙屬性的選中狀態(tài) node.setChecked(item.mAlpha == VirtualView.ALPHA_SELECTED); }

6. 響應(yīng)無(wú)障礙事件

添加了虛擬視圖的自定義控件要響應(yīng)無(wú)障礙服務(wù)的相關(guān)事件,如點(diǎn)擊

需要做下面兩個(gè)步驟:

第一步:

重寫dispatchHoverEvent()方法,并且把事件轉(zhuǎn)發(fā)給實(shí)現(xiàn)了ExploreByTouchHelper的對(duì)象處理,如下所示:

@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)@Overridepublic boolean dispatchHoverEvent(MotionEvent event) { if (mTouchHelper.dispatchHoverEvent(event)) { return true; }

第二步:

在輔助類中重寫onPerformActionForVirtualView()方法,在此方法中處理相關(guān)的無(wú)障礙事件

事件處理完成返回true,未處理返回false,從而讓系統(tǒng)自動(dòng)處理

下面的代碼中處理了點(diǎn)擊事件:

@Override protected boolean onPerformActionForVirtualView( int virtualViewId, int action, Bundle arguments) { switch (action) { case AccessibilityNodeInfoCompat.ACTION_CLICK: //處理點(diǎn)擊事件VirtualView view = findVirtualViewById(virtualViewId);if (view == null)return false;setVirtualViewSelected(view, !(view.mAlpha == VirtualView.ALPHA_SELECTED) );invalidate(); mTouchHelper.invalidateVirtualView(virtualViewId);return true; }return false; }

注意:當(dāng)虛擬節(jié)點(diǎn)中的無(wú)障礙屬性更改后,需要調(diào)用invalidateVirtualView()更新指定的無(wú)障礙節(jié)點(diǎn)

如果不更新無(wú)障礙服務(wù)獲取的信息會(huì)出現(xiàn)問(wèn)題,如焦點(diǎn)顯示錯(cuò)誤、文本提示錯(cuò)誤、狀態(tài)朗讀錯(cuò)誤等

以上就是自定義視圖實(shí)現(xiàn)虛擬節(jié)點(diǎn)的方法

借助ExploreByTouchHelper工具類實(shí)現(xiàn)虛擬節(jié)點(diǎn)我們只需要重寫對(duì)應(yīng)的方法、添加無(wú)障礙代理、轉(zhuǎn)發(fā)事件就能輕松的完成

比使用AccessibilityNodeProvider類簡(jiǎn)單很多

下面貼出完整的選擇顏色的自定義視圖代碼以供參考:

public class VirtualSubview extends View {private final Paint mPaint = new Paint(); private final Rect mTempRect = new Rect(); private final List<VirtualView> mChildren = new ArrayList<VirtualView>(); private VirtualSubview mLastHoveredChild; private CustomViewTouchHelper mTouchHelper; private Context context;public VirtualSubview(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; createVirtualChildren(); mTouchHelper = new CustomViewTouchHelper(this); ViewCompat.setAccessibilityDelegate(this, mTouchHelper); }@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) @Override public boolean dispatchHoverEvent(MotionEvent event) { if (mTouchHelper.dispatchHoverEvent(event)) { return true; }return super.dispatchHoverEvent(event); }@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { int offsetX = 0; List<VirtualView> children = mChildren; final int childCount = children.size(); for (int i = 0; i < childCount; i++) { VirtualView child = children.get(i); Rect childBounds = child.mBounds; childBounds.set(offsetX, 0, offsetX + childBounds.width(), childBounds.height()); offsetX += childBounds.width(); } }@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = 0; int height = 0; List<VirtualView> children = mChildren; final int childCount = children.size(); for (int i = 0; i < childCount; i++) { VirtualView child = children.get(i); width += child.mBounds.width(); height = Math.max(height, child.mBounds.height()); } setMeasuredDimension(width, height); }@Override protected void onDraw(Canvas canvas) { Rect drawingRect = mTempRect; List<VirtualView> children = mChildren; final int childCount = children.size(); for (int i = 0; i < childCount; i++) { VirtualView child = children.get(i); drawingRect.set(child.mBounds); mPaint.setColor(child.mColor); mPaint.setAlpha(child.mAlpha); canvas.drawRect(drawingRect, mPaint); } }@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_UP: VirtualView view = findVirtualViewByBoords(event.getX(), event.getY()); if (view == null) return true;setVirtualViewSelected(view, !(view.mAlpha == VirtualView.ALPHA_SELECTED) ); sendAccessibilityEventForVirtualView(view, AccessibilityEvent.TYPE_VIEW_CLICKED); invalidate(); mTouchHelper.invalidateVirtualView(view.mId); return true; } return super.onTouchEvent(event); }private void createVirtualChildren() { VirtualView firstChild = new VirtualView(0, new Rect(0, 0, 150, 150), Color.RED, "Virtual view 1"); mChildren.add(firstChild); VirtualView secondChild = new VirtualView(1, new Rect(0, 0, 150, 150), Color.GREEN, "Virtual view 2"); mChildren.add(secondChild); VirtualView thirdChild = new VirtualView(2, new Rect(0, 0, 150, 150), Color.BLUE, "Virtual view 3"); mChildren.add(thirdChild); }private void setVirtualViewSelected(VirtualView virtualView, boolean selected) { virtualView.mAlpha = selected ? VirtualView.ALPHA_SELECTED : VirtualView.ALPHA_NOT_SELECTED; }private void sendAccessibilityEventForVirtualView(VirtualView virtualView, int eventType) { if (mAccessibilityManager.isTouchExplorationEnabled()) { AccessibilityEvent event = AccessibilityEvent.obtain(eventType); event.setPackageName(getContext().getPackageName()); event.setClassName(virtualView.getClass().getName()); event.setSource(VirtualSubview.this, virtualView.mId); event.getText().add(virtualView.mText); getParent().requestSendAccessibilityEvent(VirtualSubview.this, event); } }private VirtualView findVirtualViewById(int id) { List<VirtualView> children = mChildren; final int childCount = children.size(); for (int i = 0; i < childCount; i++) { VirtualView child = children.get(i); if (child.mId == id) { return child; } } return null; }private class VirtualView { public static final int ALPHA_SELECTED = 255; public static final int ALPHA_NOT_SELECTED = 127; public final int mId; public final int mColor; public final Rect mBounds; public final String mText; public int mAlpha;public VirtualView(int id, Rect bounds, int color, String text) { mId = id; mColor = color; mBounds = bounds; mText = text; mAlpha = ALPHA_NOT_SELECTED; } }private class CustomViewTouchHelper extends ExploreByTouchHelper { public CustomViewTouchHelper( VirtualSubview view) { super(view); }//通過(guò)此方法的x和y參數(shù)來(lái)確定旭虛擬視圖的哪一個(gè)虛擬子視圖的虛擬id。在此方法中調(diào)用自己實(shí)現(xiàn)的通過(guò)x、y坐標(biāo)獲取id的方法,id通常是0、1、2…… @Override protected int getVirtualViewAt(float x, float y) { VirtualViewview = findVirtualViewByBoords(x, y); if (view == null) return INVALID_ID;return view.mId; }//調(diào)用此方法來(lái)確定虛擬視圖中的哪些子視圖有無(wú)障礙焦點(diǎn)。加入列表中的虛擬id的子虛擬視圖都有無(wú)障礙焦點(diǎn) @Override protected void getVisibleVirtualViews(List<Integer> virtualViewIds) { final List<VirtualView> childs = mChildren; final int count = childs.size(); for (int i = 0; i < count; i++) { VirtualView child = childs.get(i); virtualViewIds.add(child.mId); }}//在此方法中填充子虛擬視圖的無(wú)障礙事件中的屬性 @Override protected void onPopulateEventForVirtualView( int virtualViewId, AccessibilityEvent event) { //調(diào)用此方法給無(wú)障礙事件填充text字段,text字段會(huì)被TalkBack朗讀出來(lái) VirtualView item = findVirtualViewById(virtualViewId); if (item != null) event.getText().add(item.mText); }//調(diào)用此方法填充子虛擬視圖的無(wú)障礙nodeinfo的屬性 @Override protected void onPopulateNodeForVirtualView( int virtualViewId, AccessibilityNodeInfoCompat node) { //調(diào)用此方法在NodeInfo中設(shè)置子虛擬視圖的text字段,此字段會(huì)被talkback朗讀出來(lái) VirtualView item = findVirtualViewById(virtualViewId); if (item == null) return;node.setText(item.mText); Rect bounds = item.mBounds; //調(diào)用此方法設(shè)置子虛擬視圖的焦點(diǎn)大小,焦點(diǎn)大小與實(shí)際畫的視圖一致大小。此方法必須調(diào)用。 node.setBoundsInParent(bounds);//調(diào)用此方法設(shè)置nodeinfo都能處理哪些無(wú)障礙事件。調(diào)用此方法只能說(shuō)明nodeinfo能處理這些action,不是觸發(fā)action,也不是具體處理action。這里可以設(shè)置多個(gè)action。 node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);//調(diào)用此方法代表此nodeinfo節(jié)點(diǎn)是可以被選中。當(dāng)設(shè)置為true代表可以被選中,如復(fù)選框就需要設(shè)置為true。 node.setCheckable(true); node.setChecked(item.mAlpha == VirtualView.ALPHA_SELECTED); }//調(diào)用此方法具體處理無(wú)障礙事件的action。在此方法中需要根據(jù)不同的action進(jìn)行處理,當(dāng)發(fā)生click后調(diào)用普通的點(diǎn)擊處理方法,在此案例中調(diào)用的是onitemclick()方法。 @Override protected boolean onPerformActionForVirtualView(int virtualViewId, int action, Bundle arguments) { switch (action) { case AccessibilityNodeInfoCompat.ACTION_CLICK: VirtualView view = findVirtualViewById(virtualViewId); if (view == null) return false;setVirtualViewSelected(view, !(view.mAlpha == VirtualView.ALPHA_SELECTED) ); invalidate(); mTouchHelper.invalidateVirtualView(virtualViewId); return true;}return false; }}public VirtualView findVirtualViewByBoords(float x, float y) { final List<VirtualView> childs = mChildren; final int count = childs.size(); for (int i = 0; i < count; i++) { VirtualView child = childs.get(i); if (child.mBounds.contains( (int)x, (int)y)) return child; } return null; } }

更多精彩干貨:歡迎關(guān)注“無(wú)障礙實(shí)驗(yàn)室”公眾號(hào)

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

總結(jié)

以上是生活随笔為你收集整理的视图添加字段_使用ExploreByTouchHelper辅助类为自定义视图添加虚拟视图的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。