1.View.AccessibilityDelegate 用来拓展View的访问性支持,常用来做埋点等操作

  1. /**
  2. * 为每个View设置AccessibilityDelegate,监听各种事件
  3. */
  4. public class AutoTrackActivity extends AppCompatActivity {
  5. @Override
  6. public Object getSystemService(@NonNull String name) {
  7. Object object = super.getSystemService(name);
  8. if (traceEnable() && object instanceof LayoutInflater) {
  9. return new TrackLayoutInflater((LayoutInflater) object, this);
  10. }
  11. return object;
  12. }
  13. public boolean traceEnable() {
  14. return true;
  15. }
  16. public static class TrackLayoutInflater extends LayoutInflater {
  17. private final LayoutInflater mProxy;
  18. public TrackLayoutInflater(LayoutInflater proxy, Context context) {
  19. super(proxy, context);
  20. this.mProxy = proxy;
  21. }
  22. @Override
  23. public LayoutInflater cloneInContext(Context newContext) {
  24. return new TrackLayoutInflater(mProxy, newContext);
  25. }
  26. @Override
  27. public View inflate(int resource, @Nullable ViewGroup root) {
  28. View view = mProxy.inflate(resource, root);
  29. View content = view.findViewById(android.R.id.content);
  30. if (content instanceof ViewGroup) {
  31. flatViews((ViewGroup) content);
  32. }
  33. return view;
  34. }
  35. private static void bindAccessibilityDelegate(View view) {
  36. if (view != null) {
  37. view.setAccessibilityDelegate(AccessibilityDelegateWrapper.create(view, new View.AccessibilityDelegate() {
  38. @Override
  39. public void sendAccessibilityEvent(View host, int eventType) {
  40. if (eventType == AccessibilityEvent.TYPE_VIEW_CLICKED) {
  41. Log.i("csz", host.getClass().getCanonicalName() + " click");
  42. }else if (eventType == AccessibilityEvent.TYPE_VIEW_LONG_CLICKED){
  43. Log.i("csz", host.getClass().getCanonicalName() + " longClick");
  44. }
  45. super.sendAccessibilityEvent(host, eventType);
  46. }
  47. }));
  48. }
  49. }
  50. private void flatViews(ViewGroup group) {
  51. for (int i = 0; i < group.getChildCount(); i++) {
  52. View child = group.getChildAt(i);
  53. bindAccessibilityDelegate(child);
  54. if (child instanceof ViewGroup){
  55. flatViews((ViewGroup) child);
  56. }
  57. }
  58. }
  59. }
  60. }

2.View.BaseSavedState 做状态保存

  1. static class SavedState extends BaseSavedState {
  2. float progress;
  3. SavedState(Parcelable superState) {
  4. super(superState);
  5. }
  6. private SavedState(Parcel in) {
  7. super(in);
  8. progress = (Float) in.readFloat();
  9. }
  10. @Override
  11. public void writeToParcel(Parcel out, int flags) {
  12. super.writeToParcel(out, flags);
  13. out.writeValue(progress);
  14. }
  15. @Override
  16. public String toString() {
  17. return " p =" + progress + "}";
  18. }
  19. public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
  20. @Override
  21. public SavedState createFromParcel(Parcel in) {
  22. return new SavedState(in);
  23. }
  24. @Override
  25. public SavedState[] newArray(int size) {
  26. return new SavedState[size];
  27. }
  28. };
  29. }
  30. @Override
  31. public Parcelable onSaveInstanceState() {
  32. Parcelable superState = super.onSaveInstanceState();
  33. SavedState ss = new SavedState(superState);
  34. ss.progress = mProgress;
  35. return ss;
  36. }
  37. @Override
  38. public void onRestoreInstanceState(Parcelable state) {
  39. SavedState ss = (SavedState) state;
  40. super.onRestoreInstanceState(ss.getSuperState());
  41. setProgress(ss.progress);
  42. requestLayout();
  43. }

3.View.DragShadowBuilder 构建拖动阴影

  1. private void drag() {
  2. btn.setOnLongClickListener(new View.OnLongClickListener() {
  3. @Override
  4. public boolean onLongClick(View v) {
  5. v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
  6. View.DragShadowBuilder builder = new View.DragShadowBuilder(v);
  7. ClipData data = ClipData.newPlainText("label", "我是文本内容!");
  8. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
  9. v.startDragAndDrop(data, builder, v, 0);
  10. } else {
  11. v.startDrag(data, builder, v, 0);
  12. }
  13. return true;
  14. }
  15. });
  16. frame.setOnDragListener(new View.OnDragListener() {
  17. @Override
  18. public boolean onDrag(View v, DragEvent event) {
  19. //获取事件
  20. int action = event.getAction();
  21. switch (action) {
  22. case DragEvent.ACTION_DRAG_STARTED:
  23. btn.setVisibility(View.INVISIBLE);
  24. Log.d("csz", "开始拖拽");
  25. break;
  26. case DragEvent.ACTION_DRAG_ENDED:
  27. Log.d("csz", "结束拖拽");
  28. break;
  29. case DragEvent.ACTION_DRAG_ENTERED:
  30. Log.d("csz", "拖拽的view进入监听的view时");
  31. break;
  32. case DragEvent.ACTION_DRAG_EXITED:
  33. Log.d("csz", "拖拽的view离开监听的view时");
  34. frame.setBackgroundColor(Color.BLUE);
  35. break;
  36. case DragEvent.ACTION_DRAG_LOCATION:
  37. float x = event.getX();
  38. float y = event.getY();
  39. frame.setBackgroundColor(Color.RED);
  40. Log.i("csz", "拖拽的view在监听view中的位置:x =" + x + ",y=" + y);
  41. break;
  42. case DragEvent.ACTION_DROP:
  43. Log.i("csz", "释放拖拽的view");
  44. if( event.getLocalState() != null){
  45. View localState = (View) event.getLocalState();
  46. FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
  47. ((ViewGroup) localState.getParent()).removeView(localState);
  48. frame.addView(localState, layoutParams);
  49. btn.setVisibility(View.VISIBLE);
  50. }
  51. break;
  52. }
  53. return true;
  54. }
  55. });
  56. }

4.OnApplyWindowInsetsListener处理WindowInsets

  1. root.setSystemUiVisibility(root.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
  2. final int paddingTop = root.getPaddingTop();
  3. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
  4. root.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
  5. @Override
  6. public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
  7. root.setPadding(root.getPaddingLeft(), paddingTop + insets.getSystemWindowInsetTop(), root.getPaddingRight(), root.getPaddingBottom());
  8. return insets;
  9. }
  10. });
  11. //这不是绝对必要的,但确实可以解决WindowInsets的分派方式
  12. if (root.isAttachedToWindow()) {
  13. root.requestApplyInsets();
  14. } else {
  15. root.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
  16. @Override
  17. public void onViewAttachedToWindow(View v) {
  18. v.removeOnAttachStateChangeListener(this);
  19. v.requestApplyInsets();
  20. }
  21. @Override
  22. public void onViewDetachedFromWindow(View v) {
  23. }
  24. });
  25. }
  26. }

5.getGlobalVisibleRect(rect,null) 获取在根视图上的位置

6.getHitRect(rect) 获取在父视图上的位置(left,top,right,bottom)

7.设置触控和声音反馈

  1. btn.setHapticFeedbackEnabled(true);
  2. btn.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
  3. btn.playSoundEffect(SoundEffectConstants.CLICK);

8.setActivated(true) 激活视图,例如Toolbar激活后会有阴影效果

9.setDuplicateParentStateEnabled)(false) 设置父类的状态不会传递给自身,如selected,checked等

10.setClipBounds对视图裁剪,不会影响原有宽高

  1. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
  2. btn.setClipBounds(new Rect(0,0,btn.getWidth()/2,btn.getHeight()/2));
  3. }

11.StateListAnimator为view的state状态改变加动画

  1. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  2. StateListAnimator stateListAnimator = new StateListAnimator();
  3. ObjectAnimator animator = ObjectAnimator.ofFloat(iv, "alpha", 0.5f, 1.f).setDuration(500);
  4. ObjectAnimator reverseAnimator = animator.clone();
  5. reverseAnimator.setFloatValues(1.f, 0.5f);
  6. stateListAnimator.addState(new int[]{android.R.attr.state_selected}, animator);
  7. stateListAnimator.addState(new int[]{-android.R.attr.state_selected}, reverseAnimator);
  8. iv.setStateListAnimator(stateListAnimator);
  9. iv.setSelected(lefted = !lefted);
  10. }

12.ColorStateList为view的state状态改变颜色,或者TextView的字体颜色

  1. ColorStateList stateList = new ColorStateList(new int[][]{{android.R.attr.state_selected, android.R.attr.state_checked}, {-android.R.attr.state_selected, -android.R.attr.state_checked}}, new int[]{Color.BLUE, Color.RED});
  2. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
  3. ColorStateListDrawable drawable = new ColorStateListDrawable(stateList);
  4. iv.setBackground(drawable);
  5. }

下面对ViewGroup的拓展使用进行说明:

1.ViewGroup.OnHierarchyChangeListener 监听视图的添加和移除

2.updateViewLayout 更新child的LayoutParams