Android 深入(一)- 自定义View之BottomTitleImageView
时间:2022-06-09
本文章向大家介绍Android 深入(一)- 自定义View之BottomTitleImageView,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
通过本文你可以了解到自定义View的知识,同时你可以学会如何写一个属于自己的View。同时希望能够通过这个例子重新学习下自定义View的知识。
- Android 开发到一定程度的时候,可以独立开发一款App,一些基本的知识都有使用。这时候必须进行自我提高,深入的研究下Android的各个方面,不然也只能停留在初级程序员的层次,只能是一个码农啦!
- 自定义View是我们需要掌握的基本知识,在开发中一些需求使用系统内置的View不好实现,我们经常会自定义我们自己的View,同时在面试中经常被问到。下面我们就通过一个带有底部标题的自定义ImageView来学习下自定义View。
先看看效果图(效果就类似于有标题轮播图片的效果):
屏幕快照 2017-02-18 上午10.24.26.png
1.View的生命周期。
View的生命周期(来自网络)
1)从图中我们View的整个生命周期,主要由三部分操作完成分别是measure()、layout()、draw(),作用如下:
- measure:计算视图的大小。
- layout:设置视图在屏幕中显示的位置。
- draw:绘制视图。其中onDraw()方法会花费大量时间,布局变化会重绘视图,所以在onDraw()中要避免对象分配。
2)invalidate() 和requsetLaytout()作用如下:
- invalidate():重新绘制view,执行draw()操作。
- requsetLaytout():重新请求绘制view,执行measure()和layout()过程,但不执行draw()操作。
2. 定义和加载自定义属性。
1)在values文件夹中定义属性文件attrs_wcircle_view.xml。
<resources>
<declare-styleable name="WBottomTitleView">
<attr name="textString" format="string" />
<attr name="textDimension" format="dimension" />
<attr name="textColor" format="color" />
<attr name="mAlpha" format="integer" />
<attr name="mTextBgColor" format="color" />
<attr name="textDrawable" format="color|reference" />
</declare-styleable>
</resources>
- 在自定义布局的构造方法中加载自定义属性,根据属性更新画笔。
public WBottomTitleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs, defStyle);
}
private void init(AttributeSet attrs, int defStyle) {
//加载 attributes
final TypedArray typedArray = getContext().obtainStyledAttributes(
attrs, R.styleable.WBottomTitleView, defStyle, 0);
mTextString = typedArray.getString(
R.styleable.WBottomTitleView_textString);
mTextColor = typedArray.getColor(
R.styleable.WBottomTitleView_textColor,
mTextColor);
mTextDimension = typedArray.getDimension(
R.styleable.WBottomTitleView_textDimension,
mTextDimension);
mAlpha = typedArray.getInt(R.styleable.WBottomTitleView_mAlpha, mAlpha);
mTextBgColor = typedArray.getInt(R.styleable.WBottomTitleView_mTextBgColor, mTextBgColor);
if (typedArray.hasValue(R.styleable.WBottomTitleView_textDrawable)) {
mTextDrawable = typedArray.getDrawable(
R.styleable.WBottomTitleView_textDrawable);
mTextDrawable.setCallback(this);
}
typedArray.recycle();
mTextPaint = new TextPaint();
mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setTextAlign(Paint.Align.LEFT);
p = new Paint();
invalidateTextPaintAndMeasurements();
}
//根据attributes更新TextPaint
private void invalidateTextPaintAndMeasurements() {
mTextPaint.setTextSize(mTextDimension);
mTextPaint.setColor(mTextColor);
if (TextUtils.isEmpty(mTextString)) {
mTextString = "";
}
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
mTextHeight = fontMetrics.bottom;
}
- TypedArray是一个数组容器,用于存储加载的属性。记住:用完必须recycle(),不然会发生内存泄漏。
- Paint.FontMetrics解析:
图片来自网络
1)top是一行文字的上边界 2)ascent是文字可视区域的上边界 3)descent是文字可视区域的下边界 4)bottom是一行文字的下边界 5)leading是行与行之间的间距(通常为0,bottom与descent及top与ascent之间的间距足够间隔行行)
从上图中可以发现文字的可视区域在ascent与descent之间,top与bottom见的距离是整个文字所占空间的高度。
3. onDraw()绘制视图。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
int paddingRight = getPaddingRight();
int paddingBottom = getPaddingBottom();
int contentWidth = getWidth() - paddingLeft - paddingRight;
int contentHeight = getHeight() - paddingTop - paddingBottom;
mTextPaintfontMetrics = mTextPaint.getFontMetrics();
p.setColor(mTextBgColor);// 设置灰色
p.setAlpha(mAlpha);
p.setStyle(Paint.Style.FILL);//设置填满
canvas.drawRect(paddingLeft, contentHeight - (mTextPaintfontMetrics.bottom - mTextPaintfontMetrics.top), contentWidth, contentHeight, p);// 矩形
// Draw the text.
canvas.drawText(mTextString,
paddingLeft,
paddingTop + (contentHeight - mTextHeight),
mTextPaint);
// Draw the text drawable on top of the text.
if (mTextDrawable != null) {
mTextDrawable.setBounds(paddingLeft, paddingTop,
paddingLeft + contentWidth, paddingTop + contentHeight);
mTextDrawable.draw(canvas);
}
}
4. 完整代码
package cn.studyou.myviewdeep.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.ImageView;
import cn.studyou.myviewdeep.R;
/**
* 基本功能:带有底部标题的ImageView
* 创建:王杰
* 创建时间:2017-02-18
*/
public class WBottomTitleView extends ImageView {
private String mTextString;
private int mTextColor = Color.RED;
private int mAlpha = 150;
private float mTextDimension = 0;
private Drawable mTextDrawable;
private Paint p;
private TextPaint mTextPaint;
private float mTextHeight;
private int mTextBgColor = Color.DKGRAY;
private Paint.FontMetrics mTextPaintfontMetrics;
public WBottomTitleView(Context context) {
super(context);
init(null, 0);
}
public WBottomTitleView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public WBottomTitleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs, defStyle);
}
private void init(AttributeSet attrs, int defStyle) {
//加载 attributes
final TypedArray typedArray = getContext().obtainStyledAttributes(
attrs, R.styleable.WBottomTitleView, defStyle, 0);
mTextString = typedArray.getString(
R.styleable.WBottomTitleView_textString);
mTextColor = typedArray.getColor(
R.styleable.WBottomTitleView_textColor,
mTextColor);
mTextDimension = typedArray.getDimension(
R.styleable.WBottomTitleView_textDimension,
mTextDimension);
mAlpha = typedArray.getInt(R.styleable.WBottomTitleView_mAlpha, mAlpha);
mTextBgColor = typedArray.getInt(R.styleable.WBottomTitleView_mTextBgColor, mTextBgColor);
if (typedArray.hasValue(R.styleable.WBottomTitleView_textDrawable)) {
mTextDrawable = typedArray.getDrawable(
R.styleable.WBottomTitleView_textDrawable);
mTextDrawable.setCallback(this);
}
typedArray.recycle();
mTextPaint = new TextPaint();
mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setTextAlign(Paint.Align.LEFT);
p = new Paint();
invalidateTextPaintAndMeasurements();
}
//根据attributes更新TextPaint
private void invalidateTextPaintAndMeasurements() {
mTextPaint.setTextSize(mTextDimension);
mTextPaint.setColor(mTextColor);
if (TextUtils.isEmpty(mTextString)) {
mTextString = "";
}
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
mTextHeight = fontMetrics.bottom;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
int paddingRight = getPaddingRight();
int paddingBottom = getPaddingBottom();
int contentWidth = getWidth() - paddingLeft - paddingRight;
int contentHeight = getHeight() - paddingTop - paddingBottom;
mTextPaintfontMetrics = mTextPaint.getFontMetrics();
p.setColor(mTextBgColor);// 设置灰色
p.setAlpha(mAlpha);
p.setStyle(Paint.Style.FILL);//设置填满
canvas.drawRect(paddingLeft, contentHeight - (mTextPaintfontMetrics.bottom - mTextPaintfontMetrics.top), contentWidth, contentHeight, p);// 矩形
// Draw the text.
canvas.drawText(mTextString,
paddingLeft,
paddingTop + (contentHeight - mTextHeight),
mTextPaint);
// Draw the text drawable on top of the text.
if (mTextDrawable != null) {
mTextDrawable.setBounds(paddingLeft, paddingTop,
paddingLeft + contentWidth, paddingTop + contentHeight);
mTextDrawable.draw(canvas);
}
}
public String gettextString() {
return mTextString;
}
public void setTextString(String textString) {
mTextString = textString;
invalidateTextPaintAndMeasurements();
}
public int getTextColor() {
return mTextColor;
}
public void setTextColor(int textColor) {
mTextColor = textColor;
invalidateTextPaintAndMeasurements();
}
public float getTextDimension() {
return mTextDimension;
}
public void setTextDimension(float textDimension) {
mTextDimension = textDimension;
invalidateTextPaintAndMeasurements();
}
public Drawable getTextDrawable() {
return mTextDrawable;
}
public void setTextDrawable(Drawable textDrawable) {
mTextDrawable = textDrawable;
}
public int getmTextBgColor() {
return mTextBgColor;
}
public void setmTextBgColor(int mTextBgColor) {
this.mTextBgColor = mTextBgColor;
}
public int getmAlpha() {
return mAlpha;
}
public void setmAlpha(int mAlpha) {
this.mAlpha = mAlpha;
}
}
5. 开始使用自定义的View
1)在布局文件中引入View
<?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"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorAccent"
>
<cn.studyou.myviewdeep.view.WBottomTitleView
android:layout_width="match_parent"
android:layout_height="170dp"
android:id="@+id/wCircleView"
android:scaleType="fitXY"
app:textDimension="18sp"
app:textColor="#ffffff"
/>
</RelativeLayout>
- Activity中设置图片和标题
package cn.studyou.myviewdeep;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.bumptech.glide.Glide;
import butterknife.BindView;
import butterknife.ButterKnife;
import cn.studyou.myviewdeep.view.WBottomTitleView;
public class MainActivity extends AppCompatActivity {
@BindView(R.id.wCircleView)
WBottomTitleView wCircleView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
wCircleView.setTextString(" 今天天气不错,感觉挺好的。 ");
Glide.with(this).load("http://img06.tooopen.com/images/20170214/tooopen_sy_198645593736.jpg").into(wCircleView);
}
}
6. 项目地址:https://github.com/wjie2014/MyViewDeep
- MSBuild入门
- HTTP Basic Authentication验证WCF Data Service
- 移除WordPress 仪表盘首页的“插件”“其它WordPress 新闻”小工具
- 解决VMware 7在Windows 7上无法上网的问题
- Windows Server 2008群集仲裁机制
- [C#2] 5-迭代器
- 服务器未能识别 HTTP 标头 SOAPAction 的值
- 实用代码-C#获取本机网络适配器信息及MAC地址
- WordPress 自定义 login (登录页面)CSS 样式
- [C#1] 12-特性
- HTTP Basic Authentication for RESTFul Service
- [C#2] 4-可空类型、静态类
- jquery 操作css 尺寸
- Windows 7上IIS出现http 500错误
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- jQuery 效果
- jQuery 属性操作
- jQuery 文本属性值
- 10.4【前端开发】页面布局:如何理解 “CSS 视觉格式化模型”?
- Android Material UI控件之ShapeableImageView
- jQuery 元素操作
- jQuery 事件注册和事件处理
- 10.5 块级盒子模型(原盒子模型):两种盒子有什么不同?
- Android Material UI控件之MaterialButton
- jQuery (事件、拷贝)对象
- 10.6 border-color简写属性:如何理解四值语法?
- jQuery 插件
- dotnet 基于 dotnet format 的 GitHub Action 自动代码格式化机器人
- 10.7 border-width边框粗细:outline与border有什么不同?
- WPF 非客户区的触摸和鼠标点击响应