1
自定义 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
版权声明: 本文为 InfoQ 作者【Changing Lin】的原创文章。
原文链接:【http://xie.infoq.cn/article/e7b48d72ac38a62b80d8d3395】。文章转载请联系作者。
Changing Lin
关注
获得机遇的手段远超于固有常规之上~ 2020.04.29 加入
我能做的,就是调整好自己的精神状态,以最佳的面貌去面对那些未曾经历过得事情,对生活充满热情和希望。
评论