申明
自定义view简单流程
流程
- 这个也不复杂哈,我写了个ViewGroup
- 这个相比view自定义还简单一点,它复杂地方是极端子view的位置;以及大小,来判断ViewGroup的大小。
/**
* 简单的自定义viewgroup
* 这个viewGroup呢,是一个存放三个方块,然后三个方块会不断从顶部移动到底部。方块大小固定,就用TextView吧,黑底,里面有简单的文字。
*/
public class SimpleSelfLayout extends ViewGroup {
}
设置LayoutParams
//这个是设置LayoutParams的
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
onMeasure
- 这里我没有做任何操作哈
- 这个方法主要是计算childView的测量值以及模式,以及设置自己的宽和高:
//这个是设置整个容器的大小的
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//暂时默认大小好了
//正常情况呢,需要遍历子view的大小,来判断容易的大小的。
//EXACTLY:表示设置了精确的值,一般当childView设置其宽、高为精确值、match_parent时,ViewGroup会将其设置为EXACTLY;
//
//AT_MOST:表示子布局被限制在一个最大值内,一般当childView设置其宽、高为wrap_content时,ViewGroup会将其设置为AT_MOST;
//
//UNSPECIFIED:表示子布局想要多大就多大,一般出现在AadapterView的item的heightMode中、ScrollView的childView的heightMode中;此种模式比较少见。
//这三种测了模式都会影响容易的大小。
measureChildren(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getWidth(), getHeight());
}
onLayout
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
firstOnLayout(changed, l, t, r, b);
}
int[][] locations;
private void firstOnLayout(boolean changed, int l, int t, int r, int b) {
locations = new int[getChildCount()][4];//存放每个子view的坐标数据left,top,right,bottom
//设置子view的位置。
int count = getChildCount();//子view的数量
int width = 0;
int height = 0;
MarginLayoutParams params;
int maxWidth = 0;
//开始遍历子view
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
maxWidth += child.getMeasuredWidth();
}
int lastWidth = getWidth() - maxWidth;//剩余的空间,这个空间需要平均分配,让空间间距一样。
int avgWidth = lastWidth / (count + 1);
if (avgWidth < 0) avgWidth = 0;
int cLeft = 0, cTop = 0, cRight = 0, cBottom = 0;
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
width = child.getMeasuredWidth();
height = child.getMeasuredHeight();
params = (MarginLayoutParams) child.getLayoutParams();
cLeft = cRight + params.leftMargin + avgWidth;//左边距加上剩余的距离 i个子view需要分成i+1份
cRight = cLeft + params.rightMargin + width;
cTop = params.topMargin;
cBottom = height + cTop;
//设置子view的位置
locations[i][0] = cLeft;
locations[i][1] = cTop;
locations[i][2] = cRight;
locations[i][3] = cBottom;
Log.d("坐标", "cLeft = " + cLeft);
Log.d("坐标", "cTop = " + cTop);
Log.d("坐标", "cRight = " + cRight);
Log.d("坐标", "cBottom = " + cBottom);
child.layout(cLeft, cTop, cRight, cBottom);
}
letMove();
}
使用
<androidx.constraintlayout.widget.ConstraintLayout 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"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.southwind.selfview.selfview.SimpleSelfLayout
android:id="@+id/simpel_group"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="80dp"
android:layout_height="50dp"
android:background="@color/teal_700"
android:textColor="@color/white"
android:gravity="center"
android:text="1" />
<TextView
android:layout_width="80dp"
android:layout_height="50dp"
android:background="@color/teal_200"
android:textColor="@color/white"
android:gravity="center"
android:text="2" />
<TextView
android:layout_width="80dp"
android:layout_height="50dp"
android:background="@color/purple_500"
android:textColor="@color/white"
android:gravity="center"
android:text="3" />
</com.southwind.selfview.selfview.SimpleSelfLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
移动效果
- 这里我不知道开启线程循环会不会有啥坏影响,但是我确实找不到其他方法了
- 这个效果可以继续分一下,比如不同速率,分别启动等。最终形态就是金币于,下面有一个人在接着,然后算分数。
private void letMove() {
MoveRunnable runnable = new MoveRunnable();
Thread thread = new Thread(runnable);
thread.start();
}
public void stop(){
startMove = false;
}
private void setCurrentPlace() {
int top = locations[0][1] + moveHeight;
if (top > getHeight()) {
moveHeight = 0;
}
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.layout(locations[i][0], locations[0][1] + moveHeight, locations[i][2], locations[0][3] + moveHeight);
}
}
private boolean startMove = true;
private class MoveRunnable implements Runnable {
@Override
public void run() {
while (startMove) {
moveHeight += 2;
setCurrentPlace();
try {
Thread.sleep(35);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
问题
- 设置LayoutParams的时,竟然有两个方法,我一开使用的第二个,一直会出问题,不知道为什么。
- 位置计算真的头疼,算错了就会变得奇奇怪怪的。
- 跑了几分钟也没怎么样,看来这样子循环应该问题不大。
//这个是设置LayoutParams的
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateLayoutParams(LayoutParams p) {
return super.generateLayoutParams(p);
}
效果图

简单的自定义ViewGroup.gif
网友评论