自定义一个View,实现温度计的功能
看完鸿洋大大的博客其中一篇关于
自定义view的文章http://blog.csdn.net/lmj623565791/article/details/24252901,受益良多决定自己一试,并记录下自己的心得。关于编写自定义View的步骤,请大家阅读鸿洋大大的文章,在这里我就不再累述。
一、我们需要自定义温度计的属性,比如将温度计放入布局之后温度计和内包含液体的颜色,温度计的高低等等。在res/values/ 下建立一个attrs.xml , 在里面定义我们的属性和声明我们的整个样式代码如下:
liquid_color : 温度计里液体的颜色
crust_color : 温度计外壳的颜色
crust_width : 温度计外壳的宽度
crust_height : 温度计外壳的高度
init_temperature : 温度计的初始温度
二、然后我们新建一个类继承View,用以实现我们的温度计。自定义组件重点是实现View类的onDraw()和onMeasure()方法,前者用于绘制组件的外观,后者用与控制组件本身的大小,这里我的组件不需太多要求,就让他全屏覆盖,所以我在放置组件时把大小都设置成了match_parent,没有往onMeasure()填充代码。
三、这样之后我们可以在布局文件中调用此组件了。
“一定要引入 xmlns:custom="http://schemas.android.com/apk/res/com.example.customview01"我们的命名空间,后面的包路径指的是项目的package”
这是鸿洋在引用组件时写下的,但是自己写的时候发现这样在屏幕上绘制的组件显示不出来,屏幕一片空白。
后来发现这里要修改一下,android的自定义控件现在已经不需要写包名了,要写成res-auto。于是该改成这样
四、我的MainActivity也贴在这里吧,供参考
一、我们需要自定义温度计的属性,比如将温度计放入布局之后温度计和内包含液体的颜色,温度计的高低等等。在res/values/ 下建立一个attrs.xml , 在里面定义我们的属性和声明我们的整个样式代码如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="liquid_color" format="color"/>
<attr name="crust_color" format="color"/>
<attr name="crust_width" format="dimension"/>
<attr name="crust_height" format="dimension"/>
<attr name="init_temperature" format="dimension"/>
<declare-styleable name="MyThermometer">
<attr name="liquid_color"/>
<attr name="crust_color"/>
<attr name="crust_width"/>
<attr name="crust_height"/>
<attr name="init_temperature"/>
</declare-styleable>
</resources>
liquid_color : 温度计里液体的颜色
crust_color : 温度计外壳的颜色
crust_width : 温度计外壳的宽度
crust_height : 温度计外壳的高度
init_temperature : 温度计的初始温度
二、然后我们新建一个类继承View,用以实现我们的温度计。自定义组件重点是实现View类的onDraw()和onMeasure()方法,前者用于绘制组件的外观,后者用与控制组件本身的大小,这里我的组件不需太多要求,就让他全屏覆盖,所以我在放置组件时把大小都设置成了match_parent,没有往onMeasure()填充代码。
package com.thermometer.curio.thermometer;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
/**
* Created by Curio on 2016/8/20.
*/
public class MyThermometer extends View {
private int mCrustColor;
private int mLiquidColor;
private float mCrustWidth;
private float mCrustHeight;
private float mInitTemperature;
/**
* 定义温度计感温泡的位置
*/
float circleX;
float circleY;
//创建画笔
Paint p;
Paint lp;
public MyThermometer(Context context){
this(context,null);
}
public MyThermometer(Context context,AttributeSet attrs){
this(context,attrs,0);
}
/**
* 获取自定义属性
* @param context
* @param attrs
* @param defStyle
*/
public MyThermometer(Context context,AttributeSet attrs,int defStyle){
super(context,attrs,defStyle);
//获取自定义属性
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyThermometer, defStyle, 0);
int n = typedArray.getIndexCount();
for(int i = 0; i < n ; i++){
int attr = typedArray.getIndex(i);
switch(attr){
//设置默认颜色为蓝色
case R.styleable.MyThermometer_liquid_color : mLiquidColor = typedArray.getColor(attr, Color.BLUE) ;break;
//设置默认颜色为黑色
case R.styleable.MyThermometer_crust_color : mCrustColor = typedArray.getColor(attr,Color.BLACK);break;
//设置温度计默认宽
case R.styleable.MyThermometer_crust_width : mCrustWidth =typedArray.getDimensionPixelOffset(attr,
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, getResources().getDisplayMetrics())) ;break;
//设置温度计默认高
case R.styleable.MyThermometer_crust_height : mCrustHeight = typedArray.getDimensionPixelOffset(attr,
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 250, getResources().getDisplayMetrics()));break;
//设置温度计默认温度
case R.styleable.MyThermometer_init_temperature : mInitTemperature = typedArray.getDimensionPixelOffset(attr,
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0, getResources().getDisplayMetrics()));break;
}
}
typedArray.recycle();
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
}
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
/**
* 先画温度计外壳
*/
p = new Paint();
p.setStyle(Paint.Style.STROKE);
p.setColor(mCrustColor);
// p.setColor(Color.BLUE);
p.setAntiAlias(true);
p.setStrokeWidth(5);
//温度计的位置
circleX = canvas.getWidth()/2;
circleY = canvas.getHeight()-canvas.getHeight()/4;
float mRadius = mCrustWidth/2;
canvas.drawCircle(circleX, circleY, mRadius,p);
// p.setColor(Color.YELLOW);
// canvas.drawRect(0, 0, circleX, circleY, p);
canvas.drawRect(circleX - mRadius * 0.5f, circleY - mRadius * 0.86f - mCrustHeight, circleX + mRadius * 0.5f, circleY - mRadius ,p);
/**
* 画温度计里的液体
*/
lp = new Paint();
lp.setStyle(Paint.Style.FILL);
lp.setColor(mLiquidColor);
lp.setAntiAlias(true);
float mLRadius = mRadius-15;
canvas.drawCircle(circleX, circleY, mLRadius, lp);
//canvas.drawRect(circleX - mLRadius * 0.5f, circleY - mLRadius * 0.86f - 400, circleX + mLRadius * 0.5f, circleY - mLRadius *0.86f+0,lp);
lp.setStrokeWidth(mLRadius);
canvas.drawLine(circleX, circleY, circleX, circleY-100-mInitTemperature*10,lp);
}
public void setTemperature(float mTemperature){
mInitTemperature = mTemperature;
postInvalidate();
}
}
三、这样之后我们可以在布局文件中调用此组件了。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/background2"
tools:context="com.thermometer.curio.thermometer.MainActivity">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:layout_width="50dp"
android:layout_height="30dp"
android:id="@+id/input"
android:lines="1"
android:maxLength="2"
android:inputType="number"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/post"
android:text="℃更新温度"/>
</LinearLayout>
<com.thermometer.curio.thermometer.MyThermometer
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/temperature"
custom:liquid_color="@color/white"
custom:crust_color="@color/white"
custom:crust_width="50dp"
custom:crust_height="400dp"
custom:init_temperature="0dp"/>
</RelativeLayout>
“一定要引入 xmlns:custom="http://schemas.android.com/apk/res/com.example.customview01"我们的命名空间,后面的包路径指的是项目的package”
这是鸿洋在引用组件时写下的,但是自己写的时候发现这样在屏幕上绘制的组件显示不出来,屏幕一片空白。
后来发现这里要修改一下,android的自定义控件现在已经不需要写包名了,要写成res-auto。于是该改成这样
xmlns:custom="http://schemas.android.com/apk/res-auto"
四、我的MainActivity也贴在这里吧,供参考
package com.thermometer.curio.thermometer;
import android.app.Activity;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//去掉窗口标题
requestWindowFeature(Window.FEATURE_NO_TITLE);
//全屏显示
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
final EditText mInput =(EditText)findViewById(R.id.input);
final Button mPost = (Button)findViewById(R.id.post);
final MyThermometer myThermometer =(MyThermometer)findViewById(R.id.temperature);
mPost.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View source) {
if(mInput != null ){
String f = mInput.getText().toString();
if(!f.equals("")){
myThermometer.setTemperature(Float.parseFloat(f));
}
} else{
myThermometer.setTemperature(1f);
}
}
});
}
}
代码上传至远程仓库,欢迎互相学习交流,提出修改建议,最终版本在mater分支和curiousx分支上,请不要下载master分支:https://git.coding.net/CuriousXeon/MyThermometerView.git