自定义控件(27)---自定义控件之组合控件(2) 通用的类似设置界面的样子
来源:互联网 发布:nginx ip hash 编辑:程序博客网 时间:2024/06/05 14:57
一个APP中类似如下的界面
我们可以通过如下的做法来实现:
1、最基础的就是堆砌Xml布局文件
2、然后随着经验的积累,就开始用到自定义组合控件,通过获取attr里面的东东,然后去通过代码的方式去构造里面的控件,不过没有很好的扩展性,可以参考我的一篇博客链接
自定义控件(25)—自定义控件之组合控件
3、依然是获取attr里面的东西,通过移动canvas画布的方式,去构建里面的各个控件,这个方法扩展性更好
今天我们就来学习第三种用法的使用,let’s begin
首先看看demo的ui图
对应布局我就直接贴出代码了,
activity_main.xml
<?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" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.safly.ui.ItemView style="@style/SetItemStyleBase" android:background="@drawable/click_back" app:drawableStart="@drawable/option1" app:textStart="@string/personal" app:textStartLeftMargin="10dp" /> <include layout="@layout/devider" /> <com.safly.ui.ItemView style="@style/SetItemStyleBase" android:background="@drawable/click_back" app:drawableStart="@drawable/option1" app:drawableStartHeight="30dp" app:drawableStartWidth="30dp" app:textStart="@string/personal" app:textStartLeftMargin="10dp" /> <include layout="@layout/devider" /> <com.safly.ui.ItemView style="@style/SetItemStyleBase" android:layout_width="match_parent" android:layout_height="60dp" android:background="@drawable/click_back" android:paddingLeft="20dp" android:paddingRight="20dp" app:arrowDrawable="@drawable/arrow" app:arrowHeight="14dp" app:arrowWidth="8dp" app:drawableStart="@drawable/option3" app:itemStyle="arrow" app:textStart="@string/virtual" app:textStartColor="#FF3E96" app:textStartLeftMargin="10dp" app:textStartSize="20sp" /></LinearLayout>
click_back.xml
<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:drawable="@android:color/holo_blue_light"/> <item android:state_pressed="false" android:drawable="@android:color/white"/></selector>
我们看看attrs.xml里面都需要定义什么东东?
<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="ItemView"> <attr name="itemStyle" format="enum"> <enum name="normal" value="0"/> <enum name="arrow" value="1"/> </attr> <attr name="drawableStart" format="reference"/> <attr name="drawableStartWidth" format="dimension"/> <attr name="drawableStartHeight" format="dimension"/> <attr name="textStart" format="string"/> <attr name="textStartColor" format="color"/> <attr name="textStartSize" format="dimension"/> <!--绘制textStart时距离左边的偏移量--> <attr name="textStartLeftMargin" format="dimension"/> <attr name="arrowDrawable" format="reference"/> <attr name="arrowWidth" format="dimension"/> <attr name="arrowHeight" format="dimension"/> </declare-styleable></resources>
我们就直接看下ItemView是如何构造的把
package com.safly.ui;import android.content.Context;import android.content.res.Resources;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.RectF;import android.graphics.drawable.Drawable;import android.text.TextPaint;import android.util.AttributeSet;import android.util.DisplayMetrics;import android.util.Log;import android.util.TypedValue;import android.view.View;import com.safly.R;/** * Created by Administrator on 2016/11/10. */public class ItemView extends View { private static final String TAG = "ItemView"; private DisplayMetrics dm; private static final int STYLE_NORMAL = 0; private static final int STYLE_ARROW = 1; private int style = STYLE_NORMAL; private Drawable drawableStart; private int drawableStartWidth = -2,drawableStartHeight = -2; private String textStart = null; private int textStartColor = DEFAULT_TEXTCOLOR; private static final int DEFAULT_TEXTCOLOR = Color.parseColor("#5a5a5a"); private static final int DEFAULT_TEXTSIZE = 14; private int textStartSize; private int textStartLeftMargin = 0; private Drawable arrowDrawable; private int arrowWidth = -2,arrowHeight = -2; /***toggleButton默认宽*/ private static final int TOGGLEBUTTON_WIDTH = 8; /***toggleButton默认高*/ private static final int TOGGLEBUTTON_HEIGHT = 14; //默认控件的宽,匹配屏幕大小 private static final int DEFAULT_WIDTH = -1; //默认控件的高度 private static final int DEFAULT_HEIGHT = 50; private TextPaint mTextStartPaint; public ItemView(Context context) { this(context,null); } public ItemView(Context context, AttributeSet attrs) { this(context, attrs,0); } public ItemView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); dm = Resources.getSystem().getDisplayMetrics(); setEnabled(true); setClickable(true); setLongClickable(false); setup(attrs); } //初始化view的属性 private void setup(AttributeSet attrs){ TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.ItemView); if(ta != null){ try { style = ta.getInt(R.styleable.ItemView_itemStyle,STYLE_NORMAL); drawableStart = ta.getDrawable(R.styleable.ItemView_drawableStart); if(drawableStart != null){ drawableStartWidth = ta.getDimensionPixelSize( R.styleable.ItemView_drawableStartWidth,applyDimension(drawableStartWidth)); drawableStartHeight = ta.getDimensionPixelSize( R.styleable.ItemView_drawableStartHeight,applyDimension(drawableStartHeight)); } textStart = ta.getString(R.styleable.ItemView_textStart); Log.i(TAG,"textStart---------"+textStart); textStartColor = ta.getColor(R.styleable.ItemView_textStartColor, textStartColor); textStartSize = ta.getDimensionPixelSize( R.styleable.ItemView_textStartSize, applyDimension(DEFAULT_TEXTSIZE)); textStartLeftMargin = ta.getDimensionPixelOffset( R.styleable.ItemView_textStartLeftMargin, textStartLeftMargin); Log.i(TAG,"textStart---------"+textStartLeftMargin); if(style == STYLE_ARROW){ arrowDrawable = ta.getDrawable(R.styleable.ItemView_arrowDrawable); arrowWidth = ta.getDimensionPixelSize( R.styleable.ItemView_arrowWidth,applyDimension(TOGGLEBUTTON_WIDTH)); arrowHeight = ta.getDimensionPixelSize( R.styleable.ItemView_arrowHeight,applyDimension(TOGGLEBUTTON_HEIGHT)); } }catch (Exception e){ Log.e(TAG, "setup: e", e); }finally { ta.recycle(); } } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { /**计算宽**/ final int widthMode = MeasureSpec.getMode(widthMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); if(widthMode == MeasureSpec.UNSPECIFIED || widthMode == MeasureSpec.AT_MOST){ width = applyDimension(DEFAULT_WIDTH); } /**计算高**/ final int heightMode = MeasureSpec.getMode(heightMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); if (heightMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.AT_MOST) { height = applyDimension(DEFAULT_HEIGHT); } Log.i(TAG, "[onMeasure] width = " + width + ",height = " + height); setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { drawStartDrawable(canvas); drawTextStart(canvas); switch (style){ case STYLE_ARROW: drawArrowDrawable(canvas); break; } } /** * 绘制drawableStart * @param canvas */ private void drawStartDrawable(Canvas canvas){ Log.d(TAG, "drawStartDrawable: "); if (drawableStart != null){ mesureDrawableStartSize(); final float startY = (getHeight() - drawableStartHeight) / 2.0f; canvas.save(); canvas.translate(getPaddingLeft(),startY); drawableStart.setBounds(0,0,drawableStartWidth,drawableStartHeight); drawableStart.draw(canvas); canvas.restore(); } } private void mesureDrawableStartSize(){ drawableStartWidth = drawableStartWidth >=0?drawableStartWidth:drawableStart.getIntrinsicWidth(); drawableStartHeight = drawableStartHeight >= 0?drawableStartHeight:drawableStart.getIntrinsicHeight(); } /** * 绘制textStart * @param canvas *ascent = ascent线的y坐标 - baseline线的y坐标;//负数 descent = descent线的y坐标 - baseline线的y坐标;//正数 top = top线的y坐标 - baseline线的y坐标;//负数 bottom = bottom线的y坐标 - baseline线的y坐标;//正数 leading = top线的y坐标 - ascent线的y坐标;//负数 ---------------------------------------- ascent线Y坐标 = baseline线的y坐标 + fontMetric.ascent; descent线Y坐标 = baseline线的y坐标 + fontMetric.descent; top线Y坐标 = baseline线的y坐标 + fontMetric.top; bottom线Y坐标 = baseline线的y坐标 + fontMetric.bottom; */ private void drawTextStart(Canvas canvas){ Log.d(TAG, "drawTextStart: "); assumeTextStartPaint(); Paint.FontMetrics fontMetrics = mTextStartPaint.getFontMetrics(); final int left = getPaddingLeft()+drawableStartWidth+textStartLeftMargin; RectF target = new RectF(left,0,left+mTextStartPaint.measureText(textStart),getHeight()); //(fontMetrics.bottom+fontMetrics.top) = top线Y+bottom线Y-2*baseLine线Y int baseLine = (int)((target.bottom + target.top - fontMetrics.bottom - fontMetrics.top) / 2.0f); canvas.save(); canvas.translate(left,baseLine); canvas.drawText(textStart, 0, 0, mTextStartPaint); canvas.restore(); } private void assumeTextStartPaint(){ if (mTextStartPaint == null){ final Resources res = getResources(); mTextStartPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); mTextStartPaint.density = res.getDisplayMetrics().density; mTextStartPaint.setTextSize(textStartSize); mTextStartPaint.setColor(textStartColor); mTextStartPaint.setAntiAlias(true); } } private void drawArrowDrawable(Canvas canvas){ Log.d(TAG, "drawArrowDrawable: "); mesureDrawablerrowSize(); final float startY = (getHeight() - arrowHeight) / 2.0f; canvas.save(); canvas.translate(getWidth() - getPaddingRight(),startY); arrowDrawable.setBounds(0,0,arrowWidth,arrowHeight); arrowDrawable.draw(canvas); canvas.restore(); } private void mesureDrawablerrowSize(){ arrowWidth = arrowWidth >= 0?arrowWidth:arrowDrawable.getIntrinsicWidth(); arrowHeight = arrowHeight >= 0?arrowHeight:arrowDrawable.getIntrinsicHeight(); } /** * dp2px * px是安卓系统内部使用的单位 * @param value */ private int applyDimension(float value){ return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, dm); }}
接下来对上面几个重要的方法进行解释说明:
在onDraw里面是需要计算用心计算的, drawStartDrawable(canvas);这是画左边的图片
private void drawStartDrawable(Canvas canvas){ Log.d(TAG, "drawStartDrawable: "); if (drawableStart != null){ mesureDrawableStartSize(); final float startY = (getHeight() - drawableStartHeight) / 2.0f; canvas.save(); canvas.translate(getPaddingLeft(),startY); drawableStart.setBounds(0,0,drawableStartWidth,drawableStartHeight); drawableStart.draw(canvas); canvas.restore(); } } private void mesureDrawableStartSize(){ drawableStartWidth = drawableStartWidth >=0?drawableStartWidth:drawableStart.getIntrinsicWidth(); drawableStartHeight = drawableStartHeight >= 0?drawableStartHeight:drawableStart.getIntrinsicHeight(); }
以上就是获取图片的宽度、高度,然后就去计算图片的getPaddingLeft,
(getHeight() - drawableStartHeight) / 2.0f 这是计算红线的高度
然后通过drawableStart.setBounds(0,0,drawableStartWidth,drawableStartHeight);
为图片设定区域,最后画出来即可
对应第三个的小箭头是同理
我们就看下第二个文字是如何处理的?
private void drawTextStart(Canvas canvas){ Log.d(TAG, "drawTextStart: "); assumeTextStartPaint(); Paint.FontMetrics fontMetrics = mTextStartPaint.getFontMetrics(); final int left = getPaddingLeft()+drawableStartWidth+textStartLeftMargin; RectF target = new RectF(left,0,left+mTextStartPaint.measureText(textStart),getHeight()); //(fontMetrics.bottom+fontMetrics.top) = top线Y+bottom线Y-2*baseLine线Y int baseLine = (int)((target.bottom + target.top - fontMetrics.bottom - fontMetrics.top) / 2.0f); canvas.save(); canvas.translate(left,baseLine); canvas.drawText(textStart, 0, 0, mTextStartPaint); canvas.restore(); } private void assumeTextStartPaint(){ if (mTextStartPaint == null){ final Resources res = getResources(); mTextStartPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); mTextStartPaint.density = res.getDisplayMetrics().density; mTextStartPaint.setTextSize(textStartSize); mTextStartPaint.setColor(textStartColor); mTextStartPaint.setAntiAlias(true); } }
绘制文字主要就是计算baseLine,这里我就推荐个博客
http://blog.csdn.net/harvic880925/article/details/50423762
//(fontMetrics.bottom+fontMetrics.top) = top线Y+bottom线Y-2*baseLine线Y
int baseLine = (int)((target.bottom + target.top - fontMetrics.bottom - fontMetrics.top) / 2.0f);
以下是计算的结论依据
ascent = ascent线的y坐标 - baseline线的y坐标;//负数 descent = descent线的y坐标 - baseline线的y坐标;//正数 top = top线的y坐标 - baseline线的y坐标;//负数 bottom = bottom线的y坐标 - baseline线的y坐标;//正数 leading = top线的y坐标 - ascent线的y坐标;//负数 ---------------------------------------- ascent线Y坐标 = baseline线的y坐标 + fontMetric.ascent; descent线Y坐标 = baseline线的y坐标 + fontMetric.descent; top线Y坐标 = baseline线的y坐标 + fontMetric.top; bottom线Y坐标 = baseline线的y坐标 + fontMetric.bottom;
colors.xml
<?xml version="1.0" encoding="utf-8"?><resources> <color name="colorPrimary">#3F51B5</color> <color name="colorPrimaryDark">#303F9F</color> <color name="colorAccent">#FF4081</color> <color name="textColor">#5A5A5A</color> <color name="divider">#CCCCCC</color> <color name="white">#ffffff</color> <color name="red">#FFff0000</color></resources>
dimens.xml
<resources> <!-- Default screen margins, per the Android Design guidelines. --> <dimen name="activity_horizontal_margin">16dp</dimen> <dimen name="activity_vertical_margin">16dp</dimen> <dimen name="textSize1">20sp</dimen> <dimen name="textSize2">15sp</dimen> <dimen name="padding">20dp</dimen> <dimen name="margin">10dp</dimen> <dimen name="MenuitemHeight">60dp</dimen></resources>
styles.xml
<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style> <style name="SetItemStyleBase"> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">@dimen/MenuitemHeight</item> <item name="android:paddingLeft">@dimen/padding</item> <item name="android:paddingRight">@dimen/padding</item> <item name="com.safly:textStartColor">@color/textColor</item> <item name="com.safly:textStartSize">@dimen/textSize2</item> </style></resources>
devider.xml
<?xml version="1.0" encoding="utf-8"?><ImageView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="0.33dp" android:background="@color/divider"/>
- 自定义控件(27)---自定义控件之组合控件(2) 通用的类似设置界面的样子
- 自定义组合控件---设置界面条目控件
- 自定义控件(25)---自定义控件之组合控件
- 自定义控件--自定义组合控件(RelativeLayout的组合控件)
- 自定义组合控件的写法(基础)
- Android自定义控件之自定义组合控件(三)
- Android自定义控件之自定义组合控件(一)
- 自定义类似ImageView的控件
- android自定义控件(一),组合控件Titlebar的定制
- 自定义控件之组合控件
- 自定义控件之组合控件
- 自定义控件之组合控件
- Android自定义控件实例(1)——自定义控件之组合控件,包含书签的pdf阅读器
- 自定义控件(一):组合控件的使用
- Launcher-自定义实现类似android主界面的滑屏换屏控件
- 自定义实现类似android主界面的滑屏换屏控件
- 自定义实现类似android主界面的滑屏换屏控件
- 【组合控件】android自定义控件之带文字的ImageView
- Lua弱表
- JEM代码分析(一)CommonDef.h
- maven教程2-maven入门
- linux系统安装mycat,并配置读写分离
- AIZU Lowest Common Ancestor Tarjan求LCA
- 自定义控件(27)---自定义控件之组合控件(2) 通用的类似设置界面的样子
- iframe子页面调用父页面方法 跨域 异常 Blocked a frame with origin
- 如何基于报表工具FineReport进行二次开发
- XMLHttpRequest 传递中文 乱码
- 跑jar工程命令:
- 【NOIp 2003】【树结构·搜索】传染病防治
- Kendo UI头像上传 和 增删改查
- java 常用工具类 合集
- windows下配置nginx+php环境