菜鸟进阶笔记|初探自定义View|05|自定义ViewGroup
程序员文章站
2022-06-08 17:36:12
...
类似ScrollView的滑动,并附加黏性效果
自定义View中代码如下:
public class MyScrollView extends ViewGroup {
private static final String TAG = "MyScrollView";
private Scroller mScroller;
private int mScreenHeight; //获取屏幕高度
int mLastY = 0;
int mStart = 0;
public MyScrollView(Context context) {
super(context);
}
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context);
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
mScreenHeight = wm.getDefaultDisplay().getHeight(); //记录设备屏幕高度
}
public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* 在ViewGroup能够滚动之前,需要先放置好它的子View。使用遍历的方式来通知子View对自身进行测量
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int count = getChildCount();
for (int i = 0; i < count; ++i) {
View childView = getChildAt(i);
//要求其子View根据此View测量自己
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
}
}
/**
* 接下来就要对子View进行放置位置的设定。我们让每个子View都显示完整的一屏,方便后续操作。
* 在放置子View前,需要确定整个ViewGroup的高度。
* 在获取了整个ViewGroup的高度之后,就可以通过遍历来设定每个子View需要放置的位置了
* 直接通过调用子View的Layout()方法,并将具体的位置座位参数传递进去即可,代码如下所示。
* 在本例中由于让每个子View都占一屏的高度,因此整个ViewGroup的高度即子View的个数乘以屏幕的高度。
*
* @param changed
* @param l
* @param t
* @param r
* @param b
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
//设置ViewGroup的高度
MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
mlp.height = mScreenHeight * childCount;
setLayoutParams(mlp);
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
child.layout( //给子View设置位置
l,
i * mScreenHeight,
r,
(i + 1) * mScreenHeight
);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastY = y;
mStart = getScrollY();
break;
case MotionEvent.ACTION_MOVE:
if (!mScroller.isFinished()) { //确认滚动器是否完成滚动
mScroller.abortAnimation(); //停止动画
}
int dy = mLastY - y;
if (getScrollY() < 0) {
dy = 0;
}
if (getScrollY() > mScreenHeight) {
dy = 0;
}
scrollBy(0, dy);
mLastY = y;
break;
case MotionEvent.ACTION_UP:
//当手指离开后,如果手指滑动超过一定距离,则平滑到下一个View,如果小于一定距离,则回滚到原来的位置。
int mEnd = getScrollY();
int dScrollY = mEnd - mStart;
if (dScrollY > 0) {
if (dScrollY < mScreenHeight / 3) {
mScroller.startScroll(0, getScrollY(), 0, -dScrollY);
} else {
mScroller.startScroll(0, getScrollY(), 0, mScreenHeight - dScrollY);
}
} else {
if (-dScrollY < mScreenHeight / 3) {
mScroller.startScroll(0, getScrollY(), 0, -dScrollY);
} else {
mScroller.startScroll(0, getScrollY(), 0, -mScreenHeight - dScrollY);
}
}
break;
}
invalidate();
return true;
}
//该回调在执行invalidate()方法时会执行
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) { //判断是否已终止滚动
scrollTo(0, mScroller.getCurrY());
postInvalidate();
}
}
}
在xml中如下使用:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.myview.myview.MyScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorAccent" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary" />
</com.example.myview.myview.MyScrollView>
</LinearLayout>
就不贴效果图啦,跑一下就知道了(#^.^#)
上一篇: 自定义ViewGroup