一. 前言
这个控件是为了做而做,中途还是遇到了很鬼畜的问题的。是因为我在开发我的软件工程课程设计-连连看的时候,需要用到这个控件。
Github地址:NumberOfItem
二. 自定义View
1. 自定义用到的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| private ImageView show_img = null;
private int srcResource;
private int imgResource;
private TextView show_count = null;
private int countResource;
private int count = 0;
private Paint paint;
|
2. 在values文件夹下自定义属性
1 2 3 4 5 6 7 8 9 10 11
| <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="NumberOfItem"> <attr name="srcResource" format="reference"/> <attr name="imgResource" format="reference"/> <attr name="countResource" format="reference"/> </declare-styleable> </resources>
|
3. 创建构造方法
构造方法还是Xml代码创建的时候进入的构造方法重要,在其中解析属性,设置属性。
在此之前,我们需要加载我们的布局文件。
在这里,我遇到了我第一个bug,解不开的。我本来是使用Java代码在onLayout手动创建视图,第一个bug’是onLayout多次测量,我解决了。但是第二个bug很无语,单独使用一个控件是没有问题的,但是使用多个,除了第一个以外,其他的都没有效果。
所以,我在这里使用了xml代码创建布局。本来想在onLayout中修改子View位置,结果发现出现上面同样的问题。最终放弃了在onLayout中的操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content">
<RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="5dp" > <ImageView android:id="@+id/show_img" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/fight" android:scaleType="fitXY" android:background="@drawable/img_shape" /> </RelativeLayout>
<TextView android:id="@+id/show_count" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/count_shape" android:layout_alignParentEnd="true" android:layout_alignParentBottom="true" />
</RelativeLayout>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
|
public NumberOfItem(Context context,int srcResource,int imgResource,int countResource) { super(context);
init();
this.srcResource = srcResource; this.imgResource = imgResource; this.countResource = countResource;
show_img.setImageResource(srcResource); show_img.setBackgroundResource(imgResource); show_count.setBackgroundResource(countResource); }
public NumberOfItem(Context context, AttributeSet attrs) { super(context, attrs);
init();
if (attrs != null) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.NumberOfItem);
srcResource = typedArray.getResourceId( R.styleable.NumberOfItem_srcResource, R.drawable.fight ); show_img.setImageResource(srcResource); imgResource = typedArray.getResourceId( R.styleable.NumberOfItem_imgResource, R.drawable.img_shape ); show_img.setBackgroundResource(imgResource); countResource = typedArray.getResourceId( R.styleable.NumberOfItem_countResource, R.drawable.count_shape ); show_count.setBackgroundResource(countResource);
typedArray.recycle(); } }
private void init() { paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setTextSize(PxUtil.spToPx(14,getContext())); paint.setTypeface(Typeface.DEFAULT_BOLD); paint.setColor(Color.WHITE);
View inflate = View.inflate(getContext(), R.layout.number_item, this); show_img = inflate.findViewById(R.id.show_img); show_count = inflate.findViewById(R.id.show_count); }
|
4. 在onMeasure中支持wrap_content属性
设置一个默认的宽高。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int mWidth = PxUtil.dpToPx(45,getContext()); int mHeight = PxUtil.dpToPx(45,getContext());
if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT && getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
setMeasuredDimension(mWidth, mHeight); } else if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT) {
setMeasuredDimension(mWidth, heightSize); } else if (getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
setMeasuredDimension(widthSize, mHeight); } }
|
5. 绘制物品数量
对于drawText方法,可以看这篇文章:Android Canvas的drawText()和文字居中方案,尤其是其中的文字居中方案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas);
String text = String.valueOf(count);
float text_width = paint.measureText(text);
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
float text_height = (fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;
int pivotX = show_count.getLeft() + (show_count.getRight()-show_count.getLeft()) / 2; int pivotY = show_count.getTop() + (show_count.getBottom()-show_count.getTop()) / 2;
canvas.drawText(text, pivotX-text_width/2, pivotY+text_height, paint); }
public void setCount(int count) { this.count = count;
invalidate(); }
|
6. 默认的资源文件
文本数量的默认背景,这里不仅控制了其背景颜色还有大小。
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<solid android:color="#EB5476"/>
<size android:width="20dp" android:height="20dp" />
</shape>
|
视图的总体背景,这里不仅设置了背景颜色和圆角,还设置了内间距。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:radius="18dp"/>
<solid android:color="#67C1D1"/>
<padding android:left="5dp" android:top="5dp" android:right="5dp" android:bottom="5dp" />
</shape>
|
这两个文件就是我们可以自己修改的文件,从而实现自己想要的样式。
三. 具体的使用
1. NumberOfItem
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
| public class NumberOfItem extends RelativeLayout { private ImageView show_img = null; private int srcResource; private int imgResource;
private TextView show_count = null; private int countResource; private int count = 0; private Paint paint;
public NumberOfItem(Context context) { super(context);
init(); }
public NumberOfItem(Context context, AttributeSet attrs) { super(context, attrs);
init();
if (attrs != null) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.NumberOfItem);
srcResource = typedArray.getResourceId( R.styleable.NumberOfItem_srcResource, R.drawable.fight ); imgResource = typedArray.getResourceId( R.styleable.NumberOfItem_imgResource, R.drawable.img_shape ); countResource = typedArray.getResourceId( R.styleable.NumberOfItem_countResource, R.drawable.count_shape );
typedArray.recycle(); } }
private void init() { paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setTextSize(PxUtil.spToPx(14,getContext())); paint.setTypeface(Typeface.DEFAULT_BOLD); paint.setColor(Color.WHITE);
View inflate = View.inflate(getContext(), R.layout.number_item, this); show_img = inflate.findViewById(R.id.show_img); show_count = inflate.findViewById(R.id.show_count); }
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int mWidth = PxUtil.dpToPx(45,getContext()); int mHeight = PxUtil.dpToPx(45,getContext());
if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT && getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
setMeasuredDimension(mWidth, mHeight); } else if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT) {
setMeasuredDimension(mWidth, heightSize); } else if (getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
setMeasuredDimension(widthSize, mHeight); } }
@Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas);
String text = String.valueOf(count);
float text_width = paint.measureText(text);
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
float text_height = (fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;
int pivotX = show_count.getLeft() + (show_count.getRight()-show_count.getLeft()) / 2; int pivotY = show_count.getTop() + (show_count.getBottom()-show_count.getTop()) / 2;
canvas.drawText(text, pivotX-text_width/2, pivotY+text_height, paint); }
public int getCount() { return count; }
public void setCount(int count) { this.count = count;
invalidate(); }
public Paint getPaint() { return paint; }
public void setPaint(Paint paint) { this.paint = paint; }
public ImageView getShow_img() { return show_img; }
public void setShow_img(ImageView show_img) { this.show_img = show_img; }
public int getSrcResource() { return srcResource; }
public void setSrcResource(int srcResource) { this.srcResource = srcResource; }
public int getImgResource() { return imgResource; }
public void setImgResource(int imgResource) { this.imgResource = imgResource; }
public TextView getShow_count() { return show_count; }
public void setShow_count(TextView show_count) { this.show_count = show_count; }
public int getCountResource() { return countResource; }
public void setCountResource(int countResource) { this.countResource = countResource; } }
|
1 2 3 4 5 6 7 8 9 10 11
| <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="NumberOfItem"> <attr name="srcResource" format="reference"/> <attr name="imgResource" format="reference"/> <attr name="countResource" format="reference"/> </declare-styleable> </resources>
|
2. PxToUtil
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| public class PxUtil {
public static float getScreenDensity(Context context) { return context.getResources().getDisplayMetrics().density; }
public static int dpToPx(int dp, Context context){ return (int) (dp * getScreenDensity(context)); }
public static int dpToPx(float dp, Context context){ return (int) (dp * getScreenDensity(context)); }
public static float pxToDp(int px, Context context){ return (px / getScreenDensity(context)); }
public static float pxToDp(float px, Context context){ return (px / getScreenDensity(context)); }
public static int spToPx(int sp, Context context){ return (int) (sp * getScreenDensity(context)); }
public static int pxToSp(int px, Context context){ return (int) (px / getScreenDensity(context)); } }
|
3. 资源文件
count_shape.xml
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<solid android:color="#EB5476"/>
<size android:width="20dp" android:height="20dp" />
</shape>
|
Img_shape.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:radius="18dp"/>
<solid android:color="#67C1D1"/>
<padding android:left="5dp" android:top="5dp" android:right="5dp" android:bottom="5dp" />
</shape>
|
number_item.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content"> <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="5dp" > <ImageView android:id="@+id/show_img" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/fight" android:scaleType="fitXY" android:background="@drawable/img_shape" /> </RelativeLayout>
<TextView android:id="@+id/show_count" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/count_shape" android:layout_alignParentEnd="true" android:layout_alignParentBottom="true" />
</RelativeLayout>
|
4. 调用代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
NumberOfItem item = findViewById(R.id.number_item); NumberOfItem item1 = findViewById(R.id.number_item1);
item.setCount(10); } }
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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=".MainActivity">
<swu.xl.numberofitem.NumberOfItem android:id="@+id/number_item" android:layout_width="55dp" android:layout_height="55dp" />
<swu.xl.numberofitem.NumberOfItem android:id="@+id/number_item1" android:layout_width="55dp" android:layout_height="55dp" android:layout_marginStart="80dp" app:srcResource="@drawable/fight" />
<swu.xl.numberofitem.NumberOfItem android:id="@+id/number_item2" android:layout_width="55dp" android:layout_height="55dp" app:srcResource="@drawable/fight" android:layout_marginStart="160dp" />
</RelativeLayout>
|
5. 运行效果
