前言:今天闲来无事想到了刚接触安卓不久的时候特别感兴趣的一个东西,那就是自定义View属性。恰巧今天闲来无事,就写一个简单的类似于朋友圈点赞列表的小程序。
我的思路是这样的:点赞列表采用流式布局,由于用户点击名字可以查看用户资料,所以我自定义一个TextView,并定义一个展示用的username和查询资料所需要的userid。
首先,实现自定义TextView:
1、定义自定义属性
在values目录下新建xml文件attrs.xml,添加代码
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyTextView">
<attr name="username" format="string"/>
<attr name="userid" format="integer"/>
<attr name="style" format="string" >
<flag name="styleA" value="0"/>
<flag name="styleB" value="1"/>
</attr>
<attr name="check" format="string" >
<flag name="checked" value="0"/>
<flag name="unchecked" value="1"/>
</attr>
</declare-styleable>
</resources>
2、创建MyTextView并继承TextView,实现构造函数和onDraw方法(onMeasure跟onLayout用不到就不需重写了)
public class MyTextView extends TextView{
private String username;
private int userid = -1;
private String style;
private String checked;
public MyTextView(Context context) {
super(context);
}
public MyTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.MyTextView);
username = ta.getString(R.styleable.MyTextView_username);
userid = ta.getInt(R.styleable.MyTextView_userid,-1);
style = ta.getString(R.styleable.MyTextView_style);
checked = ta.getString(R.styleable.MyTextView_check);
Log.e("checked",checked+"s");
ta.recycle();
}
public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.MyTextView);
username = ta.getString(R.styleable.MyTextView_username);
userid = ta.getInt(R.styleable.MyTextView_userid,-1);
style = ta.getString(R.styleable.MyTextView_style);
checked = ta.getString(R.styleable.MyTextView_check);
ta.recycle();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(style.equals("0x0")){
setText(username+",");
}else{
setText(username);
}
if(checked.equals("0x0")){
setTextColor(Color.BLUE);
}else{
setTextColor(Color.GRAY);
}
}
public String getUserName(){
return username;
}
public int getUserId(){
return userid;
}
}
TextView创建完了,则创建流式布局(从网上找了一个直接套过来的)
public class MyFlowLayout extends ViewGroup {
public MyFlowLayout(Context context) {
this(context, null);
}
public MyFlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyFlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private List<List<View>> mLineViews = new ArrayList<List<View>>();
private List<Integer> mLineHeight = new ArrayList<Integer>();
/**
* 测量所有子View大小,确定ViewGroup的宽高
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//由于onMeasure会执行多次,避免重复的计算控件个数和高度,这里需要进行清空操作
mLineViews.clear();
mLineHeight.clear();
//获取测量的模式和尺寸大小
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec)-getPaddingLeft()-getPaddingRight();
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec)+getPaddingTop()+getPaddingBottom();
//记录ViewGroup真实的测量宽高
int viewGroupWidth = 0-getPaddingLeft()-getPaddingRight();
int viewGroupHeight = getPaddingTop()+getPaddingBottom();
if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {
viewGroupWidth = widthSize;
viewGroupHeight = heightSize;
} else {
//当前所占的宽高
int currentLineWidth = 0;
int currentLineHeight = 0;
//用来存储每一行上的子View
List<View> lineView = new ArrayList<View>();
int childViewsCount = getChildCount();
for (int i = 0; i < childViewsCount; i++) {
View childView = getChildAt(i);
//对子View进行测量
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
MarginLayoutParams marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();
int childViewWidth = childView.getMeasuredWidth() + marginLayoutParams.leftMargin + marginLayoutParams.rightMargin;
int childViewHeight = childView.getMeasuredHeight() + marginLayoutParams.topMargin + marginLayoutParams.bottomMargin;
if (currentLineWidth + childViewWidth > widthSize) {
//当前行宽+子View+左右外边距>ViewGroup的宽度,换行
viewGroupWidth = Math.max(currentLineWidth, widthSize);
viewGroupHeight += currentLineHeight;
//添加行高
mLineHeight.add(currentLineHeight);
//添加行对象
mLineViews.add(lineView);
//new新的一行
lineView = new ArrayList<View>();
//添加行对象里的子View
lineView.add(childView);
currentLineWidth = childViewWidth;
} else {
//当前行宽+子View+左右外边距<=ViewGroup的宽度,不换行
currentLineWidth += childViewWidth;
currentLineHeight = Math.max(currentLineHeight, childViewHeight);
//添加行对象里的子View
lineView.add(childView);
}
if (i == childViewsCount - 1) {
//最后一个子View的时候
//添加行对象
mLineViews.add(lineView);
viewGroupWidth = Math.max(childViewWidth, viewGroupWidth);
viewGroupHeight += childViewHeight;
//添加行高
mLineHeight.add(currentLineHeight);
}
}
}
setMeasuredDimension(viewGroupWidth, viewGroupHeight);
}
/**
* 设置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 left = getPaddingLeft();
int top = getPaddingTop();
//一共有几行
int lines = mLineViews.size();
for (int i = 0; i < lines; i++) {
//每行行高
int lineHeight = mLineHeight.get(i);
//行内有几个子View
List<View> viewList = mLineViews.get(i);
int views = viewList.size();
for (int j = 0; j < views; j++) {
View view = viewList.get(j);
MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams();
int vl = left + marginLayoutParams.leftMargin;
int vt = top + marginLayoutParams.topMargin;
int vr = vl + view.getMeasuredWidth();
int vb = vt + view.getMeasuredHeight();
view.layout(vl, vt, vr, vb);
left += view.getMeasuredWidth() + marginLayoutParams.leftMargin + marginLayoutParams.rightMargin;
}
left = getPaddingLeft();
top += lineHeight;
}
}
/**
* 指定ViewGroup的LayoutParams
*
* @param attrs
* @return
*/
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
}
接下来就是在流式布局中添加MyTextView,我这里是静态添加的,可以在JAVA代码中通过addView实现动态添加,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:background="#ffffff"
android:orientation="vertical">
<com.example.administrator.testdemo.MyFlowLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<TextView
android:layout_width="match_parent"
android:layout_height="4dp"
/>
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@drawable/dz1"/>
<com.example.administrator.testdemo.MyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
app:username="KOBE BRYANT"
app:userid="24"
android:text="1234"
app:style="styleA"
app:check="checked"
android:onClick="click"
/>
<com.example.administrator.testdemo.MyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:username="LEBORN JAMES"
app:userid="23"
android:text="1234"
app:style="styleA"
app:check="checked"
android:onClick="click"
/>
<com.example.administrator.testdemo.MyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:username="ALLEN IVERSON"
app:userid="3"
android:text="1234"
app:style="styleA"
app:check="checked"
android:onClick="click"
/>
<com.example.administrator.testdemo.MyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:username="STEVE NASH"
app:userid="10"
android:text="1234"
app:style="styleA"
app:check="checked"
android:onClick="click"
/>
<com.example.administrator.testdemo.MyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:username="VINCE CATER"
app:userid="15"
android:text="1234"
app:style="styleA"
app:check="checked"
android:onClick="click"
/>
<com.example.administrator.testdemo.MyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:username="TRACY MCGRADY"
app:userid="1"
android:text="1234"
app:style="styleA"
app:check="checked"
android:onClick="click"
/>
<com.example.administrator.testdemo.MyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:username="DWGHIT HOWARD"
app:userid="12"
android:text="1234"
app:style="styleA"
app:check="checked"
android:onClick="click"
/>
<com.example.administrator.testdemo.MyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:username="KEVIN GANNET"
app:userid="21"
android:text="1234"
app:style="styleA"
app:check="checked"
android:onClick="click"
/>
<com.example.administrator.testdemo.MyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:username="PAUL GASOL"
app:userid="16"
android:text="1234"
app:style="styleB"
app:check="checked"
android:onClick="click"
/>
</com.example.administrator.testdemo.MyFlowLayout>
</LinearLayout>
最后就是事件的处理,在这个程序中只是点击显示username和userid,代码很简单
public void click(View view) {
MyTextView mView = (MyTextView) view;
Toast.makeText(MainActivity.this,mView.getUserName()+","+mView.getUserId(),Toast.LENGTH_SHORT).show();
}
效果图: