写点什么

自定义 View:如何手写 ViewGroup 实现 ListView 效果

作者:Changing Lin
  • 2021 年 11 月 20 日
  • 本文字数:2649 字

    阅读完需:约 9 分钟

1.知识点

  • ViewGroup.onLayout

  • 如何计算每一个子 View 的位置

  • 如何计算滑动的边界

2.原理

  • onLayout 方法是在 ViewGroup 的布局阶段执行,在这里对子 View 进行布局

  • 定义一个 sumChildHeight 属性,用来存储所有子 View 的总高度,在 onLayout 中进行累加

  • 如下代码所示:

float dy = downY - event.getY() + downScrollY; // 计算在Y轴上的偏移后的目标位置int maxLength = sumChildHeight - getHeight(); // 由于sumChildHeight表示所有子View的总高度,因此,需要减去ViewGroup的高度就是最大的Y轴滑动距离
复制代码

3.代码

  • VirtualListView.java

package com.besmart.components;
import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;import android.view.ViewConfiguration;import android.view.ViewGroup;import android.widget.OverScroller;
public class VirtualListView extends ViewGroup { private static final String TAG = "VirtualListView";

float downX; float downY; float downScrollY; // 我当前滑动 ViewConfiguration viewConfiguration; OverScroller overScroller;
int sumChildHeight = 0;
public VirtualListView(Context context, AttributeSet attrs) { super(context, attrs); viewConfiguration = ViewConfiguration.get(context); overScroller = new OverScroller(context); }
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int count = getChildCount(); Log.e(TAG, String.format("onLayout: 当前有几个子View %d-%d-%d-%d-%d", count, l, t, r, b));
int childLeft = l; int childRight = r; int childTop = 0; int childBottom = 0;
sumChildHeight = 0;
for (int i = 0; i < count; i++) { View child = getChildAt(i); sumChildHeight += child.getMeasuredHeight(); childBottom += child.getMeasuredHeight();
child.layout(childLeft, childTop, childRight, childBottom);
childTop = sumChildHeight; }
}
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { measureChildren(widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec); }
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.e(TAG, "onInterceptTouchEvent: "+ev.toString()); boolean result = false; switch (ev.getActionMasked()){ case MotionEvent.ACTION_DOWN: downX = ev.getX(); downY = ev.getY(); downScrollY = getScrollY(); // 初始值 break; case MotionEvent.ACTION_MOVE: if (Math.abs(ev.getY()-downY) > viewConfiguration.getScaledPagingTouchSlop()){ getParent().requestDisallowInterceptTouchEvent(true); // 请求父控件禁止拦截事件 result = true; // 开始拦截事件 } break; default:break; } return result; }
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getActionMasked()){ case MotionEvent.ACTION_DOWN: downX = event.getX(); downY = event.getY(); downScrollY = getScrollY(); // 初始值 break; case MotionEvent.ACTION_MOVE: float dy = downY - event.getY() + downScrollY; int maxLength = sumChildHeight - getHeight(); if (dy > maxLength) dy = maxLength; else if (dy<0){ dy = 0; } scrollTo(0, (int) dy); break; case MotionEvent.ACTION_UP:// overScroller.startScroll(0, getScrollY(), 0, event.getY() > downY? getScrollY()-100:getScrollY()+100);// postInvalidateOnAnimation(); break; default:break; } return true; }
@Override public void computeScroll() {// if (overScroller.computeScrollOffset()){// scrollTo(overScroller.getCurrX(), overScroller.getCurrY());// postInvalidateOnAnimation();// } }}
复制代码
  • activity_touch.xml

<?xml version="1.0" encoding="utf-8"?><com.besmart.components.VirtualListView    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context=".EmptyActivity">
<com.example.hellojnicallback.TestView android:background="#ff0000" android:layout_width="match_parent" android:layout_height="280dp"/>
<View android:background="#00ff00" android:layout_width="match_parent" android:layout_height="300dp"/>
<View android:background="#0000ff" android:layout_width="match_parent" android:layout_height="200dp"/>
<View android:background="#00f0ff" android:layout_width="match_parent" android:layout_height="220dp"/>
<View android:background="#f000ff" android:layout_width="match_parent" android:layout_height="100dp"/>
</com.besmart.components.VirtualListView>
复制代码


发布于: 3 小时前阅读数: 6
用户头像

Changing Lin

关注

获得机遇的手段远超于固有常规之上~ 2020.04.29 加入

我能做的,就是调整好自己的精神状态,以最佳的面貌去面对那些未曾经历过得事情,对生活充满热情和希望。

评论

发布
暂无评论
自定义View:如何手写ViewGroup实现ListView效果