Android定制组件的三种方式

2024-06-18 02:18

本文主要是介绍Android定制组件的三种方式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原文地址: http://www.cnblogs.com/baihai/archive/2012/08/20/2647533.html

方式一、继承原有的组件

实例:带图像的TextView

1、IconTextView.java

复制代码
package net.blogjava.mobile.widget;import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.TextView;public class IconTextView extends TextView
{//  命名空间的值private final String namespace = "http://net.blogjava.mobile";//  图像资源IDprivate int resourceId = 0;private Bitmap bitmap;//构造函数public IconTextView(Context context, AttributeSet attrs){//继承TextView的构造函数public TextView(Context context, AttributeSet attrs)super(context, attrs);resourceId = attrs.getAttributeResourceValue(namespace, "iconSrc", 0);if (resourceId > 0)bitmap = BitmapFactory.decodeResource(getResources(), resourceId);}@Overrideprotected void onDraw(Canvas canvas){if (bitmap != null){            //  从原图上截取图像的区域,在本例中为整个图像Rect src = new Rect();//  将截取的图像复制到bitmap上的目标区域,在本例中与复制区域相同Rect target = new Rect();src.left = 0;src.top = 0;src.right = bitmap.getWidth();src.bottom = bitmap.getHeight();int textHeight = (int) getTextSize();target.left = 0;//  计算图像复制到目录区域的纵坐标。由于TextView中文本内容并不是从最顶端开始绘制的,因此,需要重新计算绘制图像的纵坐标target.top = (int) ((getMeasuredHeight() - getTextSize()) / 2) + 1;target.bottom = target.top + textHeight;//  为了保证图像不变形,需要根据图像高度重新计算图像的宽度target.right = (int) (textHeight * (bitmap.getWidth() / (float) bitmap.getHeight()));//  开始绘制图像
            canvas.drawBitmap(bitmap, src, target, getPaint());//  将TextView中的文本向右移动一定的距离(在本例中移动了图像宽度加2个象素点的位置)    canvas.translate(target.right + 2, 0);}super.onDraw(canvas);// 必须最后调用}
}
复制代码

2、main.xml

复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:mobile="http://net.blogjava.mobile" android:orientation="vertical"android:layout_width="fill_parent" android:layout_height="fill_parent"><net.blogjava.mobile.widget.IconTextViewandroid:layout_width="fill_parent" android:layout_height="wrap_content"android:text="第一个笑脸" mobile:iconSrc="@drawable/small" /><net.blogjava.mobile.widget.IconTextViewandroid:layout_width="fill_parent" android:layout_height="wrap_content"android:text="第二个笑脸" android:textSize="24dp" mobile:iconSrc="@drawable/small" /><net.blogjava.mobile.widget.IconTextViewandroid:layout_width="fill_parent" android:layout_height="wrap_content"android:text="第三个笑脸" android:textSize="36dp" mobile:iconSrc="@drawable/small" /><net.blogjava.mobile.widget.IconTextViewandroid:layout_width="fill_parent" android:layout_height="wrap_content"android:text="第四个笑脸" android:textSize="48dp" mobile:iconSrc="@drawable/small" /><net.blogjava.mobile.widget.IconTextViewandroid:layout_width="fill_parent" android:layout_height="wrap_content"android:text="第五个笑脸" android:textSize="36dp" mobile:iconSrc="@drawable/small" /><net.blogjava.mobile.widget.IconTextViewandroid:layout_width="fill_parent" android:layout_height="wrap_content"android:text="第六个笑脸" android:textSize="24dp" mobile:iconSrc="@drawable/small" /><net.blogjava.mobile.widget.IconTextViewandroid:layout_width="fill_parent" android:layout_height="wrap_content"android:text="第七个笑脸" mobile:iconSrc="@drawable/small" /></LinearLayout>  
复制代码

3、效果

 

方式二、组合原有的组件

实例:带文本标签的EditText

1、LabelEditText.java

复制代码
package net.blogjava.mobile.widget;import net.blogjava.mobile.R;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
import android.widget.TextView;public class LabelEditText extends LinearLayout {private TextView textView;private String labelText;private int labelFontSize;private String labelPosition;public LabelEditText(Context context, AttributeSet attrs) {super(context, attrs);// 读取labelText属性的资源IDint resourceId = attrs.getAttributeResourceValue(null, "labelText", 0);// 未获得资源ID,继续读取属性值if (resourceId == 0)labelText = attrs.getAttributeValue(null, "labelText");// 从资源文件中获得labelText属性的值elselabelText = getResources().getString(resourceId);// 如果按两种方式都未获得labelTex属性的值,表示未设置该属性,抛出异常if (labelText == null) {throw new RuntimeException("必须设置labelText属性.");}// 获得labelFontSize属性的资源IDresourceId = attrs.getAttributeResourceValue(null, "labelFontSize", 0);// 继续读取labelFontSize属性的值,如果未设置该属性,将属性值设为14if (resourceId == 0)labelFontSize = attrs.getAttributeIntValue(null, "labelFontSize", 14);// 从资源文件中获得labelFontSize属性的值elselabelFontSize = getResources().getInteger(resourceId);// 获得labelPosition属性的资源IDresourceId = attrs.getAttributeResourceValue(null, "labelPosition", 0);// 继续读取labelPosition属性的值if (resourceId == 0)labelPosition = attrs.getAttributeValue(null, "labelPosition");// 从资源文件中获得labelPosition属性的值elselabelPosition = getResources().getString(resourceId);// 如果未设置labelPosition属性值,将该属性值设为leftif (labelPosition == null)labelPosition = "left";String infService = Context.LAYOUT_INFLATER_SERVICE;LayoutInflater li;// 获得LAYOUT_INFLATER_SERVICE服务li = (LayoutInflater) context.getSystemService(infService);LinearLayout linearLayout = null;// 根据labelPosition属性的值装载不同的布局文件if ("left".equals(labelPosition))linearLayout = (LinearLayout) li.inflate(R.layout.labeledittext_horizontal, this);else if ("top".equals(labelPosition))linearLayout = (LinearLayout) li.inflate(R.layout.labeledittext_vertical, this);elsethrow new RuntimeException("labelPosition属性的值只能是left或top.");// 下面的代码从相应的布局文件中获得了TextView对象,并根据LabelTextView的属性值设置TextView的属性textView = (TextView) findViewById(R.id.textview);textView.setTextSize((float) labelFontSize);textView.setText(labelText);}}
复制代码

2、main.xml

复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"android:layout_height="fill_parent"android:orientation="vertical" ><TextViewandroid:id="@+id/textView"/><net.blogjava.mobile.widget.LabelEditTextandroid:id="@+id/first"android:layout_width="fill_parent"android:layout_height="wrap_content"labelFontSize="16"labelPosition="left"labelText="姓名:" /><net.blogjava.mobile.widget.LabelEditTextandroid:id="@+id/second"android:layout_width="fill_parent"android:layout_height="wrap_content"android:layout_marginTop="20dp"labelFontSize="26"labelPosition="top"labelText="兴趣爱好" /></LinearLayout>
复制代码

3、labeledittext_horizontal.xml

复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="horizontal" android:layout_width="fill_parent"android:layout_height="fill_parent"><TextView android:id="@+id/textview" android:layout_width="wrap_content"android:layout_height="wrap_content" /><EditText android:id="@+id/edittext" android:layout_width="fill_parent"android:layout_height="wrap_content" />
</LinearLayout>
复制代码

4、labeledittext_vertical.xml

复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="fill_parent"android:layout_height="fill_parent"><TextView android:id="@+id/textview" android:layout_width="wrap_content"android:layout_height="wrap_content" /><EditText android:id="@+id/edittext" android:layout_width="fill_parent"android:layout_height="wrap_content" />
</LinearLayout>
复制代码

5、效果

 

方式三、完全重写组件

实例:更换变盘指针后的时钟

1、HandClocl.java

复制代码
package net.blogjava.mobile.widget;import java.util.Calendar;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.View;public class HandClock extends View implements Runnable {private int clockImageResourceId;private Bitmap bitmap;private float scale;private float handCenterWidthScale;private float handCenterHeightScale;private int minuteHandSize;private int hourHandSize;private Handler handler = new Handler();public void run() {// 重新绘制View
        invalidate();// 重新设置定时器,在60秒后调用run方法handler.postDelayed(this, 60 * 1000);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);// 根据图像的实际大小等比例设置View的大小setMeasuredDimension((int) (bitmap.getWidth() * scale), (int) (bitmap.getHeight() * scale));}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);Paint paint = new Paint();paint.setAntiAlias(true);Rect src = new Rect();Rect target = new Rect();src.left = 0;src.top = 0;src.right = bitmap.getWidth();src.bottom = bitmap.getHeight();target.left = 0;target.top = 0;target.bottom = (int) (src.bottom * scale);target.right = (int) (src.right * scale);// 画表盘图像
        canvas.drawBitmap(bitmap, src, target, paint);// 计算表盘中心点的横纵坐标float centerX = bitmap.getWidth() * scale * handCenterWidthScale;float centerY = bitmap.getHeight() * scale * handCenterHeightScale;// 表表盘中心点画一个半径为5的实心圆圈canvas.drawCircle(centerX, centerY, 5, paint);// 设置分针为3个象素粗paint.setStrokeWidth(3);Calendar calendar = Calendar.getInstance();int currentMinute = calendar.get(Calendar.MINUTE);int currentHour = calendar.get(Calendar.HOUR);// 计算分针和时间的角度double minuteRadian = Math.toRadians((360 - ((currentMinute * 6) - 90)) % 360);double hourRadian = Math.toRadians((360 - ((currentHour * 30) - 90)) % 360 - (30 * currentMinute / 60));// 在表盘上画分针canvas.drawLine(centerX, centerY, (int) (centerX + minuteHandSize * Math.cos(minuteRadian)), (int) (centerY - minuteHandSize * Math.sin(minuteRadian)), paint);// 设置实针为4个象素粗paint.setStrokeWidth(4);// 在表盘上画分针canvas.drawLine(centerX, centerY, (int) (centerX + hourHandSize * Math.cos(hourRadian)), (int) (centerY - hourHandSize * Math.sin(hourRadian)), paint);}public HandClock(Context context, AttributeSet attrs) {super(context, attrs);// 读取相应的属性值clockImageResourceId = attrs.getAttributeResourceValue(null, "clockImageSrc", 0);if (clockImageResourceId > 0)bitmap = BitmapFactory.decodeResource(getResources(), clockImageResourceId);scale = attrs.getAttributeFloatValue(null, "scale", 1);handCenterWidthScale = attrs.getAttributeFloatValue(null, "handCenterWidthScale", bitmap.getWidth() / 2);handCenterHeightScale = attrs.getAttributeFloatValue(null, "handCenterHeightScale", bitmap.getHeight() / 2);// 在读取分针和时针长度后,将其值按图像的缩放比例进行缩放minuteHandSize = (int) (attrs.getAttributeIntValue(null, "minuteHandSize", 0) * scale);hourHandSize = (int) (attrs.getAttributeIntValue(null, "hourHandSize", 0) * scale);int currentSecond = Calendar.getInstance().get(Calendar.SECOND);// 将定时器设在0分时执行run方法handler.postDelayed(this, (60 - currentSecond) * 1000);}@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();// 删除回调类handler.removeCallbacks(this);}}
复制代码

完整代码:http://pan.baidu.com/share/link?shareid=22336&uk=1829692564

这篇关于Android定制组件的三种方式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1071033

相关文章

SpringBoot中@Value注入静态变量方式

《SpringBoot中@Value注入静态变量方式》SpringBoot中静态变量无法直接用@Value注入,需通过setter方法,@Value(${})从属性文件获取值,@Value(#{})用... 目录项目场景解决方案注解说明1、@Value("${}")使用示例2、@Value("#{}"php

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

Vue和React受控组件的区别小结

《Vue和React受控组件的区别小结》本文主要介绍了Vue和React受控组件的区别小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录背景React 的实现vue3 的实现写法一:直接修改事件参数写法二:通过ref引用 DOMVu

JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法

《JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法》:本文主要介绍JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法,每种方法结合实例代码给大家介绍的非常... 目录引言:为什么"相等"判断如此重要?方法1:使用some()+includes()(适合小数组)方法2

HTTP 与 SpringBoot 参数提交与接收协议方式

《HTTP与SpringBoot参数提交与接收协议方式》HTTP参数提交方式包括URL查询、表单、JSON/XML、路径变量、头部、Cookie、GraphQL、WebSocket和SSE,依据... 目录HTTP 协议支持多种参数提交方式,主要取决于请求方法(Method)和内容类型(Content-Ty

Android协程高级用法大全

《Android协程高级用法大全》这篇文章给大家介绍Android协程高级用法大全,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友跟随小编一起学习吧... 目录1️⃣ 协程作用域(CoroutineScope)与生命周期绑定Activity/Fragment 中手

使用shardingsphere实现mysql数据库分片方式

《使用shardingsphere实现mysql数据库分片方式》本文介绍如何使用ShardingSphere-JDBC在SpringBoot中实现MySQL水平分库,涵盖分片策略、路由算法及零侵入配置... 目录一、ShardingSphere 简介1.1 对比1.2 核心概念1.3 Sharding-Sp

Spring创建Bean的八种主要方式详解

《Spring创建Bean的八种主要方式详解》Spring(尤其是SpringBoot)提供了多种方式来让容器创建和管理Bean,@Component、@Configuration+@Bean、@En... 目录引言一、Spring 创建 Bean 的 8 种主要方式1. @Component 及其衍生注解

python中的显式声明类型参数使用方式

《python中的显式声明类型参数使用方式》文章探讨了Python3.10+版本中类型注解的使用,指出FastAPI官方示例强调显式声明参数类型,通过|操作符替代Union/Optional,可提升代... 目录背景python函数显式声明的类型汇总基本类型集合类型Optional and Union(py

Linux系统管理与进程任务管理方式

《Linux系统管理与进程任务管理方式》本文系统讲解Linux管理核心技能,涵盖引导流程、服务控制(Systemd与GRUB2)、进程管理(前台/后台运行、工具使用)、计划任务(at/cron)及常用... 目录引言一、linux系统引导过程与服务控制1.1 系统引导的五个关键阶段1.2 GRUB2的进化优