效果图:
ViewGroup子类:
- 创建过程:
1,构造方法
2,添加子View方法
3,测量(onMeasure),确认父类size
4,调整子View的布局(onLayout)
/**
* 创建过程:
* 1,构造方法
* 2,添加子View方法
* 3,测量(onMeasure),确认父类size
* 4,调整子View的布局(onLayout)
*/
public class VGTest extends ViewGroup {
//存放传过来的子View数据
private List<String> mData; //用于存储所有子View的内容
private List<List<View>> mLines; //集合内的每个元素代表每一行。
public VGTest(Context context) {
this(context,null);
}
public VGTest(Context context, AttributeSet attrs) {
this(context,attrs,0);
}
public VGTest(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mData=new ArrayList<>();
mLines=new ArrayList<>();
}
//添加子View
public void setView(List<String> data) {
mData.clear();
mData.addAll(data);
//先清空原来的内容
removeAllViews();
for (String datum : data) {
TextView textView=new TextView(getContext());
textView.setText(datum);
addView(textView);
}
}
//测量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//得到父类提供的大小
int parentWidth=MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
//获得子View个数
int childCount = getChildCount();
if(childCount==0){
return;
}
//先清空
mLines.clear();
//添加默认行
List<View> newLine=new ArrayList<>();
mLines.add(newLine);
int width = MeasureSpec.makeMeasureSpec(parentWidth, MeasureSpec.AT_MOST);
int height = MeasureSpec.makeMeasureSpec(parentHeight, MeasureSpec.AT_MOST);
for(int i=0;i<childCount;i++){
//获得子View个体
View childAt = getChildAt(i);
//如果这个View在new时设置是不可见的,则直接跳到下一个循环
if(childAt.getVisibility()!=VISIBLE){
continue;
}
measureChild(childAt,width,height);
if(newLine.size()==0){
//可以添加
newLine.add(childAt);
}else{
//判断是否可以添加进该行(计算组合宽度是否超出限定宽度,若超出则需要换行)
boolean isCanBeAdd = checkChildCanBeAdd(newLine,childAt,parentWidth);
if(isCanBeAdd){
newLine.add(childAt);
}else {
newLine=new ArrayList<>();
mLines.add(newLine);
}
}
}
//根据尺寸j计算所有行高
View child = getChildAt(0);
int childHeight = child.getMeasuredHeight();
int totalChildHeight = childHeight * mLines.size();
setMeasuredDimension(parentWidth,totalChildHeight);
}
//判断是否可以添加进该行
private boolean checkChildCanBeAdd(List<View> newLine, View childAt, int parentWidth) {
int totalWidth=0;
for (View view : newLine) {
totalWidth+=view.getMeasuredWidth();
}
totalWidth+=childAt.getMeasuredWidth();
//如果超出限制宽度,则不可以再添加(换行)
//否则可以添加
return totalWidth<=parentWidth;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// View childAt = getChildAt(0);
// childAt.layout(0,0,childAt.getMeasuredWidth(),childAt.getMeasuredHeight());
// View childAt1 = getChildAt(1);
// childAt1.layout(childAt.getRight(),0,childAt.getLeft()+childAt1.getMeasuredWidth(),childAt.getLeft()+childAt1.getMeasuredHeight());
// ......
View fristChild=getChildAt(0);
int currentLeft=0;
int currentRight=0;
int currentTop=0;
int currentBottom=fristChild.getMeasuredHeight();
for (List<View> mLine : mLines) {
for (View child : mLine) {
int width = child.getMeasuredWidth();
int height =child.getMeasuredHeight();
currentRight+=width;
currentBottom+=height;
child.layout(currentLeft,currentTop,currentRight,currentBottom);
currentLeft=currentRight;
}
currentTop+=getChildAt(0).getMeasuredHeight();
currentLeft=0;
currentRight=0;
currentBottom+=fristChild.getMeasuredHeight();
}
}
}
Main:
public class MainActivity extends AppCompatActivity {
private final String TAG="MainActivity:";
private VGTest test;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
test=findViewById(R.id.vg_test);
Log.e(TAG+"----------------------------",test+"");
List<String> data=new ArrayList<>();
data.add("asdasd");
data.add("zxczxc");
data.add("qweqwe");
data.add("fghrty");
data.add("123123");
data.add("456456");
data.add("789789");
data.add("aiopiop");
data.add("jkljkl");
data.add("p[]p[]");
Log.e("TAG",data.size()+"");
test.setView(data);
}
}
进阶实践:
效果图:
ViewGroup子类:
public class VGTest extends ViewGroup {
//存放传过来的子View数据
private List<String> mData; //用于存储所有子View的内容
private List<List<View>> mLines; //集合内的每个元素代表每一行。
private int leftMargin=15; //每个子View间的左边间距
private int toptMargin=5; //每个子View间的与上面子View的间距
private OnTextClickListener textListener;
//-----构造方法---------
public VGTest(Context context) {
this(context,null);
}
public VGTest(Context context, AttributeSet attrs) {
this(context,attrs,0);
}
public VGTest(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mData=new ArrayList<>();
mLines=new ArrayList<>();
}
//--------------------
//添加子View
public void setView(List<String> data) {
mData.clear();
mData.addAll(data);
//先清空原来的内容
removeAllViews();
for (String datum : data) {
final TextView textView= (TextView) LayoutInflater.from(getContext()).inflate(R.layout.text_demo_style,this,false);
textView.setText(datum);
final String text=datum;
//---点击事件监听-----
textView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(textListener!=null) {
textListener.getItemTextContext(v, text);
}
}
});
addView(textView);
}
}
//测量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//得到父类提供的大小
int parentWidth=MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
//获得子View个数
int childCount = getChildCount();
if(childCount==0){
return;
}
//先清空
mLines.clear();
//添加默认行
List<View> newLine=new ArrayList<>();
mLines.add(newLine);
int width = MeasureSpec.makeMeasureSpec(parentWidth, MeasureSpec.AT_MOST);
int height = MeasureSpec.makeMeasureSpec(parentHeight, MeasureSpec.AT_MOST);
for(int i=0;i<childCount;i++){
//获得子View个体
View childAt = getChildAt(i);
//如果这个View在new时设置是不可见的,则直接跳到下一个循环
if(childAt.getVisibility()!=VISIBLE){
continue;
}
measureChild(childAt,width,height);
if(newLine.size()==0){
//可以添加
newLine.add(childAt);
}else{
//判断是否可以添加进该行(计算组合宽度是否超出限定宽度,若超出则需要换行)
boolean isCanBeAdd = checkChildCanBeAdd(newLine,childAt,parentWidth);
if(!isCanBeAdd){
newLine=new ArrayList<>();
mLines.add(newLine);
}
newLine.add(childAt);
}
}
//根据尺寸j计算所有行高
View child = getChildAt(0);
int childHeight = child.getMeasuredHeight();
int totalChildHeight = childHeight * mLines.size()+toptMargin*(mLines.size()+1)+getPaddingBottom()+getPaddingTop();
setMeasuredDimension(parentWidth,totalChildHeight);
}
//判断是否可以添加进该行
private boolean checkChildCanBeAdd(List<View> newLine, View childAt, int parentWidth) {
int totalWidth=leftMargin+getPaddingLeft();
for (View view : newLine) {
totalWidth+=view.getMeasuredWidth()+leftMargin;
}
totalWidth+=childAt.getMeasuredWidth()+leftMargin+getPaddingRight();
//如果超出限制宽度,则不可以再添加(换行)
//否则可以添加
return totalWidth<=parentWidth;
}
//布局子View
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
View fristChild=getChildAt(0);
//用于标记子View的位置
int currentLeft=leftMargin+getPaddingLeft();
int currentRight=leftMargin+getPaddingLeft();
int currentTop=toptMargin+getPaddingTop();
int currentBottom=fristChild.getMeasuredHeight()+toptMargin+getPaddingTop();
//循环每一行
for (List<View> mLine : mLines) {
//对一行内的View进行布局
for (View child : mLine) {
int width = child.getMeasuredWidth();
currentRight+=width;
Log.e("TAG-------------",""+currentRight+"----------"+width);
Log.e("TAG-------------",""+(getMeasuredWidth()-leftMargin));
if(currentRight >(getMeasuredWidth()-leftMargin-getPaddingRight())){
Log.e("TAG-------------","asdasd");
currentRight = getMeasuredWidth()-leftMargin-getPaddingRight();
}
//为子View排放位置
child.layout(currentLeft,currentTop,currentRight,currentBottom);
currentLeft=currentRight+leftMargin;
currentRight+=leftMargin;
}
currentLeft=leftMargin+getPaddingLeft();
currentRight=leftMargin+getPaddingLeft();
currentTop+=getChildAt(0).getMeasuredHeight()+toptMargin;
currentBottom+=fristChild.getMeasuredHeight()+toptMargin;
}
}
//-------设置监听-----
public void setOnTextListener(OnTextClickListener textListener){
this.textListener=textListener;
};
//-------监听接口-----
public interface OnTextClickListener{
void getItemTextContext(View view,String text);
}
}
R.layout.text_demo_style:
自定义TextView(background,textColor代码就不贴出来了,自己再写一份)
<TextView
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="汉堡包"
android:gravity="center"
android:paddingLeft="15dp"
android:paddingTop="15dp"
android:paddingBottom="15dp"
android:paddingRight="15dp"
android:background="@drawable/text_demo_press"
android:textColor="@drawable/text_color_text"
android:singleLine="true"
android:maxLength="5"
android:ellipsize="end">
</TextView>
对布局文件加入Padding属性:
<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"
android:background="#000000"
tools:context=".MainActivity"
android:orientation="vertical">
<com.example.vgdemo.VGTest
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="25dp"
android:id="@+id/vg_test"/>
</LinearLayout>