自定义View
自定义View
园林美的概念及特征
• 园林美的特征
– 园林中的自然美
• 植物、大自然的山川草木、风云雨雪、日月星辰、虫鱼鸟兽以及大自然晦明、阴晴、晨昏、 昼夜、春秋的瞬息变化、声音美等等。
– 园林中的生活美
• 首先应该使园林的空气清新,无污染,水体清透无异味,卫生条件良好; • 第二,要有宜人的小气候,使气温、湿度、风等综合作用达到理想的要求。 • 第三要避免噪音。要避免噪音的干扰就要在规划时深入研究场地环境,根据具体情况设置防
景
•景
– 是以自然物为主体所形成的,能引起美感的审美 对象,而且必定是以时空为特点(景的最佳观赏 时间、空间)的多维空间,具有诗情画意,令人 赏新悦目,使人流连。
• 园林中的景
– 是指在园林绿地中,自然或经人工创造的,以能 引起人的美感为特征的一种供作游憩观赏的空间 环境。
• 杭州西湖十景(断桥残雪、苏堤春晓、平湖秋月、三 潭映月、柳浪闻莺、雷峰夕照、曲院风荷、双峰插云、 花港观鱼、南屏晚钟)、燕京八景、圆明园四十景、 避暑山庄七十二、连续的、逐渐 的变化。
• 例如自然界中一年四季的季相变化;天穹中自天空到 地平线的色彩变化;人的视野由近到远,物体从清晰 到模糊的过程,建筑墙面由于光源影响所呈现的由明 到暗以及色彩上逐渐的转变等均属之。
图3-4 韵律与节奏
园林构图的基本规律
• 多样与统一
护林或采取消音和隔音的处理。 • 第四植物种类要丰富,生长健壮繁茂,形成立体景观。 • 第五要有方便的交通,完善的生活福利设施,适合园林的文化娱乐活动和美丽安静的休息环
境。
– 园林中的艺术美
桂林山水甲天下
园林构图的基本规律
• 园林绿地构图的含义
– 在一定的空间内,结合各种园林绿地的功能要 求对各种构景要素的取舍、剪裁、配布以及组 合称为园林绿地艺术构图。
Android自定义View实现水平带数字百分比进度条
Android⾃定义View实现⽔平带数字百分⽐进度条这个进度条可以反映真实进度,并且完成百分⽐的⽂字时随着进度增加⽽移动的,所在位置也恰好是真实完成的百分⽐位置,效果如下:思路如下:第⼀部分是左侧的蓝⾊直线,代表已经完成的进度;第⼆部分是右侧灰⾊的直线,代表未完成的进度;第三部分是红⾊的百分⽐的数字百分⽐⽂本,显⽰当前确切的完成进度。
最关键的部分就是要确定百分⽐⽂本的确切位置,这⾥⽤了paint的getTextBounds⽅法,得到⽂本的宽⾼,然后再精确确定它的位置。
view代码如下:public class NumberProgressView extends View {/*** 进度条画笔的宽度(dp)*/private int paintProgressWidth = 3;/*** ⽂字百分⽐的字体⼤⼩(sp)*/private int paintTextSize = 20;/*** 左侧已完成进度条的颜⾊*/private int paintLeftColor = 0xff67aae4;/*** 右侧未完成进度条的颜⾊*/private int paintRightColor = 0xffaaaaaa;/*** 百分⽐⽂字的颜⾊*/private int paintTextColor = 0xffff0077;/*** Contxt*/private Context context;/*** 主线程传过来进程 0 - 100*/private int progress;/*** 得到⾃定义视图的宽度*/private int viewWidth;/*** 得到⾃定义视图的Y轴中⼼点*/private int viewCenterY;/*** 画左边已完成进度条的画笔*/private Paint paintleft = new Paint();/*** 画右边未完成进度条的画笔*/private Paint paintRight = new Paint();/*** 画中间的百分⽐⽂字的画笔private Paint paintText = new Paint();/*** 要画的⽂字的宽度*/private int textWidth;/*** 画⽂字时底部的坐标*/private float textBottomY;/*** 包裹⽂字的矩形*/private Rect rect = new Rect();/*** ⽂字总共移动的长度(即从0%到100%⽂字左侧移动的长度)*/private int totalMovedLength;public NumberProgressView(Context context, AttributeSet attrs) {super(context, attrs);this.context = context;// 构造器中初始化数据initData();}/*** 初始化数据*/private void initData() {//设置进度条画笔的宽度int paintProgressWidthPx = Utils.dip2px(context, paintProgressWidth);//设置百分⽐⽂字的尺⼨int paintTextSizePx = Utils.sp2px(context, paintTextSize);// 已完成进度条画笔的属性paintleft.setColor(paintLeftColor);paintleft.setStrokeWidth(paintProgressWidthPx);paintleft.setAntiAlias(true);paintleft.setStyle(Style.FILL);// 未完成进度条画笔的属性paintRight.setColor(paintRightColor);paintRight.setStrokeWidth(paintProgressWidthPx);paintRight.setAntiAlias(true);paintRight.setStyle(Style.FILL);// 百分⽐⽂字画笔的属性paintText.setColor(paintTextColor);paintText.setTextSize(paintTextSizePx);paintText.setAntiAlias(true);paintText.setTypeface(Typeface.DEFAULT_BOLD);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec);getWidthAndHeight();}/*** 得到视图等的⾼度宽度尺⼨数据*/private void getWidthAndHeight() {//得到包围⽂字的矩形的宽⾼paintText.getTextBounds("000%", 0, "000%".length(), rect);textWidth = rect.width();textBottomY = viewCenterY + rect.height() / 2;//得到⾃定义视图的⾼度int viewHeight = getMeasuredHeight();viewWidth = getMeasuredWidth();viewCenterY = viewHeight / 2;totalMovedLength = viewWidth - textWidth;}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//得到float型进度float progressFloat = progress / 100.0f;//当前⽂字移动的长度float currentMovedLentgh = totalMovedLength * progressFloat;//画左侧已经完成的进度条,长度为从Veiw左端到⽂字的左侧canvas.drawLine(0, viewCenterY, currentMovedLentgh, viewCenterY, paintleft);//画右侧未完成的进度条,这个进度条的长度不是严格按照百分⽐来缩放的,因为⽂字的长度会变化,所以它的长度缩放⽐例也会变化 if (progress < 10) {canvas.drawLine(currentMovedLentgh + textWidth * 0.5f, viewCenterY, viewWidth, viewCenterY, paintRight);} else if (progress < 100) {canvas.drawLine(currentMovedLentgh + textWidth * 0.75f, viewCenterY, viewWidth, viewCenterY, paintRight);} else {canvas.drawLine(currentMovedLentgh + textWidth, viewCenterY, viewWidth, viewCenterY, paintRight);}//画⽂字(注意:⽂字要最后画,因为⽂字和进度条可能会有重合部分,所以要最后画⽂字,⽤⽂字盖住重合的部分)canvas.drawText(progress + "%", currentMovedLentgh, textBottomY, paintText);}/*** @param progress 外部传进来的当前进度*/public void setProgress(int progress) {this.progress = progress;invalidate();}}调⽤者activity的代码,设置进度条的进度:public class NumberProgressBarActivity extends Activity {protected static final int WHAT_INCREASE = 1;private NumberProgressView np_numberProgressBar;private int progress;private Handler handler = new Handler() {public void handleMessage(android.os.Message msg) {progress++;np_numberProgressBar.setProgress(progress);handler.sendEmptyMessageDelayed(WHAT_INCREASE, getRadomNumber(50, 200));if (progress >= 100) {handler.removeMessages(WHAT_INCREASE);}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(yout.activity_number_progress_bar);np_numberProgressBar = (NumberProgressView) findViewById(R.id.np_numberProgressBar);Button btn_numberProgressBar = (Button) findViewById(R.id.btn_numberProgressBar);btn_numberProgressBar.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {increase();}});}private void increase() {progress = 0;np_numberProgressBar.setProgress(0);handler.removeMessages(WHAT_INCREASE);handler.sendEmptyMessage(WHAT_INCREASE);}/*** 得到两个整数之间的⼀个随机数** @param start 较⼩的数* @param end 较⼤的数* @return*/public int getRadomNumber(int start, int end) {return (int) (start + Math.random() * (end - start));}}⼯具⽅法:/*** 将dip或dp值转换为px值,保证尺⼨⼤⼩不变*/public static int dip2px(Context context, float dipValue) {final float scale = context.getResources().getDisplayMetrics().density;return (int) (dipValue * scale + 0.5f);}/*** 将sp值转换为px值,保证⽂字⼤⼩不变*/public static int sp2px(Context context, float spValue) {final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;return (int) (spValue * fontScale + 0.5f);}布局:<LinearLayout xmlns:android="/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><com.example.viewdemo.view.NumberProgressViewandroid:id="@+id/np_numberProgressBar"android:layout_width="wrap_content"android:layout_height="100dp"android:layout_margin="20dp"android:background="#33890075"/><Buttonandroid:id="@+id/btn_numberProgressBar"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="开始"/></LinearLayout>以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
为什么你的自定义View wrap_content不起作用?
为什么你的自定义View wrap_content不起作用?前言自定义View是Android开发中非常常用的知识可是,在使用过程中,有些开发者会发现:为什么自定义View 中设置的wrap_content属性不起作用(与match_parent相同作用)?今天,我将全面分析上述问题并给出解决方案。
1. 问题描述在使用自定义View时,View宽/ 高的wrap_content属性不起自身应有的作用,而且是起到与match_parent相同作用。
wrap_content与match_parent区别:1. wrap_content:视图的宽/高被设定成刚好适应视图内容的最小尺寸2. match_parent:视图的宽/高被设置为充满整个父布局(在Android API 8之前叫作fill_parent)其实这里有两个问题:问题1:wrap_content属性不起自身应有的作用问题2:wrap_content起到与match_parent相同的作用问题分析问题出现在View的宽/ 高设置,那我们直接来看自定义View绘制中第一步对View宽/ 高设置的过程:measure过程中的onMeasure()方法onMeasure()protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//参数说明:View的宽/ 高测量规格//setMeasuredDimension() 用于获得View宽/高的测量值//这两个参数是通过getDefaultSize()获得的setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));}继续往下看getDefaultSize()getDefaultSize()作用:根据View宽/高的测量规格计算View的宽/高值源码分析如下:public static int getDefaultSize(int size, int measureSpec) {//参数说明:// 第一个参数size:提供的默认大小// 第二个参数:宽/高的测量规格(含模式& 测量大小)//设置默认大小int result = size;//获取宽/高测量规格的模式& 测量大小int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);switch (specMode) {// 模式为UNSPECIFIED时,使用提供的默认大小// 即第一个参数:sizecase MeasureSpec.UNSPECIFIED:result = size;break;// 模式为AT_MOST,EXACTL Y时,使用View测量后的宽/高值// 即measureSpec中的specSizecase MeasureSpec.AT_MOST:case MeasureSpec.EXACTL Y:result = specSize;break;}//返回View的宽/高值return result;}从上面发现:在getDefaultSize()的默认实现中,当View的测量模式是AT_MOST或EXACTL Y时,View 的大小都会被设置成子View MeasureSpec的specSize。
Android自定义view制作抽奖转盘
Android⾃定义view制作抽奖转盘本⽂实例为⼤家分享了Android⾃定义view制作抽奖转盘的具体代码,供⼤家参考,具体内容如下效果图TurntableActivitypackage com.bawei.myapplication.turntable;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.MotionEvent;import android.view.View;import android.view.animation.RotateAnimation;import com.bawei.myapplication.R;import com.bawei.myapplication.turntable.CustomTurntableView;/*** 转盘* @author hasee*/public class TurntableActivity extends AppCompatActivity {CustomTurntableView customTurntableView;boolean isTouchInSide = false;float mDownX, mDownY;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(yout.activity_turntable);initView();}private void initView() {customTurntableView = findViewById(R.id.custom);// findViewById(R.id.custom_inside).setOnClickListener(new View.OnClickListener() {// @Override// public void onClick(View v) {// float degrees = (float)(720 + Math.random() * 1000);// RotateAnimation rotateAnimation = new RotateAnimation(0, -degrees, 450, 450); // rotateAnimation.setDuration(5000);// rotateAnimation.setFillAfter(true);// customCircleView.startAnimation(rotateAnimation);// }// });findViewById(R.id.custom_inside).setOnTouchListener(new View.OnTouchListener() { @Overridepublic boolean onTouch(View v, MotionEvent event) {if (event.getAction() == MotionEvent.ACTION_DOWN &&event.getX() > 200 &&event.getX() < 300 &&event.getY() > 200 &&event.getY() < 300) {isTouchInSide = true;mDownX = event.getX();mDownY = event.getY();return true;}else if(event.getAction() == MotionEvent.ACTION_MOVE && (event.getX() < mDownX -10 ||event.getX() > mDownX + 10 ||event.getY() < mDownY -10 ||event.getY() > mDownY + 10) ){isTouchInSide = false;} else if (event.getAction() == MotionEvent.ACTION_UP &&event.getX() > mDownX -10 &&event.getX() < mDownX + 10 &&event.getY() > mDownY -10 &&event.getY() < mDownY + 10 &&isTouchInSide) {float degrees = (float) (720 + Math.random() * 1000);RotateAnimation rotateAnimation = new RotateAnimation(0, -degrees, 250, 250);rotateAnimation.setDuration(5000);rotateAnimation.setFillAfter(true);customTurntableView.startAnimation(rotateAnimation);}isTouchInSide = false;return false;}});}}对应的布局<?xml version="1.0" encoding="utf-8"?><RelativeLayoutxmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:id="@+id/ll"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".MainActivity"><com.bawei.myapplication.turntable.CustomTurntableViewandroid:id="@+id/custom"android:layout_width="wrap_content"android:layout_height="500dp"/><com.bawei.myapplication.turntable.CustomTurntableInsideViewandroid:id="@+id/custom_inside"android:layout_width="wrap_content"android:layout_height="500dp"app:text="开始"android:background="#3300ff00" /></RelativeLayout>⾃定义CustomTurntableView继承viewpackage com.bawei.myapplication.turntable;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.graphics.RectF;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.view.View;/*** 这⾥是画转盘的* @author hasee*/public class CustomTurntableView extends View{Paint mPaint;int mCircleCount = 6;float mStartAngle = 0;RectF rectF;public CustomTurntableView(Context context) {super(context);init();}public CustomTurntableView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init();}private void init(){mPaint = new Paint();mPaint.setColor(Color.BLUE);mPaint.setStrokeWidth(10);mPaint.setTextSize(60);mPaint.setStyle(Paint.Style.FILL);rectF = new RectF();rectF.top = 100;rectF.left = 100;rectF.right = 400;rectF.bottom = 400;}String[] textColor = {"⼀等奖","⼆等奖","三等奖","四等奖","五等奖","六等奖"}; @Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);for(int i = 0; i < mCircleCount; i++){//按⾓标单双号设置扇形颜⾊,if(i % 2 == 0 ){mPaint.setColor(Color.BLUE);}else{mPaint.setColor(Color.GREEN);}canvas.drawArc(rectF, mStartAngle, 60, true, mPaint);//设置转盘上的⽂字mPaint.setColor(Color.BLACK);mPaint.setTextSize(20);Path path = new Path();path.addArc(rectF,mStartAngle+20,60);canvas.drawTextOnPath(textColor[i],path,-10,40,mPaint);mStartAngle += 60;}}}⾃定义CustomTurntableInsideView继承viewpackage com.bawei.myapplication.turntable;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.RectF;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.view.View;import com.bawei.myapplication.R;/*** 转盘中间开始按钮和指针* @author hasee*/public class CustomTurntableInsideView extends View {/*** 画笔*/Paint mPaint;RectF mRectF;String mStr;public CustomTurntableInsideView(Context context) {super(context);init();}public CustomTurntableInsideView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);//⾃定义属性,如何添加⾃定义属性如下(考点)//第⼀步:在values⽂件夹下创建attrs.xml//第⼆步:详见attrs.xml⽂件内部//第三步:在所在的布局⽂件的根layout中添加xmlns:app="/apk/res-auto"//第四步:在布局⽂件的控件中添加app:"你在attrs中设置的attr name"="你的值"//第五步:调⽤下⾯这句话,最后的为R.styleable.你在attrs中设置的declare-styleable nameTypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomTurntableView);//第六步:调⽤下⾯这句话,根据你在attrs中设置的format,选择getXXX⽅法,//⼊参为 R.styleable. 加上你在attrs中设置的declare-styleable name 加上 _ 加上你在attrs中设置的attr name mStr = typedArray.getString(R.styleable.CustomTurntableView_text);init();}private void init() {//以下注释请看CustomBingView⾥⾯mPaint = new Paint();mPaint.setColor(Color.RED);mPaint.setStrokeWidth(10);mPaint.setTextSize(20);mPaint.setStyle(Paint.Style.FILL);mRectF = new RectF();mRectF.top = 50;mRectF.bottom = 300;mRectF.right = 300;mRectF.left = 200;}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);// setMeasuredDimension(300, 300);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//设置画笔颜⾊为⿊⾊,mPaint.setColor(Color.BLACK);//画出指针,⽤⼀个扇形,然后盖住后⾯补分来简单表⽰canvas.drawArc(mRectF, 60, 60, true, mPaint);mPaint.setColor(Color.RED);//画⼀个红⾊的圆形,就是中间的⼤按钮canvas.drawCircle(250, 250, 50, mPaint);mPaint.setColor(Color.BLACK);//添加按钮上的⽂字canvas.drawText(mStr, 230, 260, mPaint);//画三⾓,第⼀步,创建路径// Path path = new Path();//第⼆步,moveTo第⼀个顶点// path.moveTo(300, 300);//后续相继lineTo其他顶点// path.lineTo(300, 400);// path.lineTo(400, 400);//闭合// path.close();// 画// canvas.drawPath(path, mPaint);}}⾃定义属性attrs.xml<?xml version="1.0" encoding="utf-8"?><resources><!-- name为想要调⽤这个属性的类名即可 --><declare-styleable name="CustomTurntableView"><!-- name为属性的名字,可以随意起,只要符合规则看得懂 --><!-- format为属性内容的类型 --><attr name="text" format="string"></attr></declare-styleable></resources>以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持,关注公众号的更多精彩内容。
Kotlin自定义View系列教程之标尺控件(选择身高、体重等)的实现
Kotlin⾃定义View系列教程之标尺控件(选择⾝⾼、体重等)的实现前⾔本篇⽂章讲的是Kotlin ⾃定义view之实现标尺控件Ruler,以选择⾝⾼、体重等。
开发中,当我们需要获取⽤户的⾝⾼和体重等信息时,如果直接让他们输⼊,显然体验不够好。
像类似于唯品会、好轻等APP都是使⽤了类似于刻度尺的控件让⽤户滑动选择⾝⾼体重,觉得很棒。
⽹上已有⼈使⽤Java语⾔实现这样的功能,但不影响我对其的学习。
和往常⼀样,主要还是想总结⼀下⾃定义view之实现标尺控件的开发过程以及⼀些需要注意的地⽅。
按照惯例,我们先来看看效果图⼀、先总结下⾃定义View的步骤:1、⾃定义View的属性2、在View的构造⽅法中获得我们⾃定义的属性3、重写onMesure4、重写onDraw其中onMesure⽅法不⼀定要重写,但⼤部分情况下还是需要重写的⼆、View 的⼏个构造函数1、constructor(mContext: Context)—>java代码直接new⼀个RulerView实例的时候,会调⽤这个只有⼀个参数的构造函数;2、constructor(mContext: Context, attrs: AttributeSet)—>在默认的XML布局⽂件中创建的时候调⽤这个有两个参数的构造函数。
AttributeSet类型的参数负责把XML布局⽂件中所⾃定义的属性通过AttributeSet带⼊到View内;3、constructor(mContext: Context, attrs: AttributeSet,defStyleAttr:Int)—>构造函数中第三个参数是默认的Style,这⾥的默认的Style是指它在当前Application或者Activity所⽤的Theme中的默认Style,且只有在明确调⽤的时候才会调⽤4、constructor(mContext: Context, attrs: AttributeSet,defStyleAttr:Int,defStyleRes:Int)—>该构造函数是在API21的时候才添加上的三、下⾯我们就开始来看看代码啦1、⾃定义View的属性,⾸先在res/values/ 下建⽴⼀个attrs.xml ,在⾥⾯定义我们的需要⽤到的属性以及声明相对应属性的取值类型<android.support.constraint.ConstraintLayout xmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:id="@+id/tv_weight_tip"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="体重"android:textColor="@android:color/black"android:textSize="14dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.132" /><RelativeLayoutandroid:id="@+id/rl_weight_ruler"android:layout_width="match_parent"android:layout_height="wrap_content"app:layout_constraintTop_toBottomOf="@+id/tv_weight_tip"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"><per.lijuan.rulerdome.RulerViewandroid:id="@+id/ruler_weight"android:layout_width="match_parent"android:layout_height="58dp"android:layout_marginTop="24dp"app:alphaEnable="true"app:lineColor="@android:color/darker_gray"app:lineMaxHeight="40dp"app:lineMidHeight="30dp"app:lineMinHeight="20dp"app:lineSpaceWidth="10dp"app:lineWidth="2.5dp"app:textColor="@android:color/black"app:minValue="20"app:maxValue="200"app:perValue="0.1"app:selectorValue="55"/><ImageViewandroid:layout_width="14dp"android:layout_height="46dp"android:layout_centerHorizontal="true"android:layout_marginTop="6dp"android:scaleType="fitXY"android:src="@mipmap/ic_arrow"/></RelativeLayout><TextViewandroid:id="@+id/tv_weight"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="11dp"android:maxHeight="30sp"android:textColor="@color/colorPrimary"android:textSize="24sp"app:layout_constraintTop_toBottomOf="@+id/rl_weight_ruler"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"/></android.support.constraint.ConstraintLayout>3、在View的构造⽅法中,获得我们的⾃定义的样式private var mMinVelocity:Int = 0private var mScroller: Scroller? = null//Scroller是⼀个专门⽤于处理滚动效果的⼯具类⽤mScroller记录/计算View滚动的位置,再重写View的computeScroll(),完成实际的滚动 private var mVelocityTracker: VelocityTracker?=null//主要⽤跟踪触摸屏事件(flinging事件和其他gestures⼿势事件)的速率。
Android中自定义样式与View的构造函数中的第三个参数defStyle的意义
零、序一、自定义Style二、在XML中为属性声明属性值1. 在layout中定义属性2. 设置Style3. 通过Theme指定三、在运行时获取属性值1. View的第三个构造函数的第三个参数defStyle2. obtailStyledAttributes3. Example四、结论与代码下载零、序系统自带的View可以在xml中配置属性,对于写的好的Custom View同样可以在xml中配置属性,为了使自定义的View的属性可以在xml中配置,需要以下4个步骤:1.1.通过<declare-styleable>为自定义View添加属性2.在xml中为相应的属性声明属性值3.在运行时(一般为构造函数)获取属性值4.将获取到的属性值应用到View怎么将获取到的属性值应用到View就不用说了,自己定义的属性什么用处自己肯定是清楚的,所以接下来看一下前三点。
通过<declare-styleable>元素声明Custom View需要的属性即可,下面是一个例子,文件是res/values/attrs.xml<?xml version="1.0" encoding="utf-8"?><resources><declare-styleable name="Customize"><attr name="attr_one" format="string"/><attr name="attr_two" format="string"/><attr name="attr_three" format="string"/><attr name="attr_four" format="string"/></declare-styleable><attr name="CustomizeStyle" format="reference"/></resources>在上述xml中,我们声明了Customize与CustomizeSyle,Customize包含了attr_one、attr_two、attr_three与attr_four四个attribute,CustomizeStyle也是一个attribute,但是却没有声明在declare-styleable中。
自定义View
要自定view就要首先明白android中的原生的View是如何实现的。
1、我们写好布局文件后加载layout xml文件。
比如linearLayout布局中包含了几个textView那么他会加载那几个textView的xml文件并且会将textView的对象创建出来。
当上面的加载做完以后就会回掉@Overrideprotected void onFinishInflate() {}方法。
在该方法中我们可以获得textView的控件对象TextView tv = getChildAt(0);等等,然后获取布局参数LayoutParams params = tv.getLayoutParams(); 然后获取长宽mWidth = params.width;然后进入measure(。
,。
)方法,这里面的两个参数是它的父控件传给他的,然后他的内部调用onMeasure(...,....)方法。
在onMeasure()方法中又调用它子view的tv.measure()的测量方法,将所有的view都测量一边,最后调用setMeasuredDimension()方法设置保持自己的尺寸。
如果要修改控件的尺寸,就重写onMeasure()方法,然后更改传给子view的测量参数或者自己的测量值。
2、layout布局Layout方法内部调用了onLayout()方法,onLayout内部就是调用子View的layout方法,来设置子view的位置,// 相对性:父容器确定孩子的位置int width = mLeftView.getMeasuredWidth();int height = mLeftView.getMeasuredHeight();Log.d(TAG, "width : " + width);Log.d(TAG, "height : " + height);// 给左侧布局int lvLeft = -width;int lvTop = 0;int lvRight = 0;int lvBottom = height;yout(lvLeft, lvTop, lvRight, lvBottom);// 有width和height注意:这里measure方法在layout方法之前执行过了这时候我们可以通过getMeasuredWidth 方法来获取宽高。
自定义View实现三角形(正三角,倒三角)
⾃定义View实现三⾓形(正三⾓,倒三⾓)⾃定义的属性如下:<declare-styleable name="TriangleView"><!--模式--><attr name="tlv_mode"><!--倒三⾓--><enum name="inverted" value="0"/><!--正三⾓--><enum name="regular" value="1"/></attr><!--颜⾊--><attr name="tlv_color" format="color|reference"/></declare-styleable>具体代码如下:import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.support.annotation.IntDef;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.view.View;import ng.annotation.Retention;import ng.annotation.RetentionPolicy;/*** 三⾓形*/public class TriangleView extends View{private Paint paint;private Path path;private int color;private int mode;private final int DEFAULT_WIDTH=48;private final int DEFAULT_HEIGHT=24;private int width = 0;private int height =0;/*** 倒三⾓*/public static final int INVERTED = 0;/*** 正三⾓*/public static final int REGULAR = 1;@IntDef({INVERTED, REGULAR})@Retention(RetentionPolicy.SOURCE)public @interface ShapeMode {}public TriangleView(Context context) {this(context,null);}public TriangleView(Context context, @Nullable AttributeSet attrs) {this(context,attrs,0);}public TriangleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(context,attrs);}private void init(Context context,AttributeSet attrs){TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TriangleView);color = typedArray.getColor(R.styleable.TriangleView_tlv_color, Color.BLACK);mode = typedArray.getInt(R.styleable.TriangleView_tlv_mode, INVERTED);typedArray.recycle();paint = new Paint();paint.setColor(color);paint.setAntiAlias(true);paint.setStyle(Paint.Style.FILL);path= new Path();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec);width = measureSize(widthMeasureSpec, DEFAULT_WIDTH);height = measureSize(heightMeasureSpec, DEFAULT_HEIGHT);setMeasuredDimension(width, height);}private int measureSize(int measureSpec, int defaultSize) {int newSize = 0;int mode = MeasureSpec.getMode(measureSpec);int size = MeasureSpec.getSize(measureSpec);switch (mode) {case MeasureSpec.AT_MOST:newSize = Math.min(size, defaultSize);break;case MeasureSpec.EXACTLY:newSize = size;break;case MeasureSpec.UNSPECIFIED:newSize = defaultSize;break;}return newSize;}public void setColor(int color){this.color=color;paint.setColor(color);invalidate();}public void setMode(@ShapeMode int mode){this.mode=mode;invalidate();}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);drawTriangle(canvas);}private void drawTriangle(Canvas canvas) {if(mode==INVERTED) {path.moveTo(0f, 0f);path.lineTo(width, 0f);path.lineTo(width / 2.0f, height);}else {path.moveTo(width/2.0f,0f);path.lineTo(0,height);path.lineTo(width,height);}path.close();canvas.drawPath(path, paint);}}。
android自定义无限循环播放的viewPager。轮播ViewPager。实现循环播放。。。
android⾃定义⽆限循环播放的viewPager。
轮播ViewPager。
实现循环播放。
前⾔实际项⽬需要⼀个播放⼴告的控件,可能有多个⼴告图⽚。
每个⼀段时间更换该图⽚。
简单来说,就是⼀个 “循环播放图⽚”的控件。
1. 间隔时间更换图⽚2. ⼀般来说,图⽚切换时需要有动画效果3. 需要⽀持⼿势,⽤户开源滑动图⽚并移动,拨动到感兴趣的图⽚并查看。
4. 在⼿势过程中不再⾃动循环播放,⼿势结束后再继续播放开源库我⾃⼰写⼀个⾃定义View,托管在gitHub。
实现思路页⾯的循环思路假如我们有两张图⽚: img1,和img2,我们可以创建两个页⾯。
如下所⽰:img1, img20, 1,但是:如果我们仅仅创建两个页⾯,就会发现滑到尽头就⽆法滑动了。
想⽆限的循环滑动,就需要: 1. 需要 img1 还能向左滑动,那么img1 的所在页⾯的左侧(前⾯)也需要多放置⼀个页⾯2. 需要到达 img2 ,即到达最后⼀页时,还能向右滑动,那么,也需要再这个页⾯右侧(后⾯)多放置⼀个页⾯据此,那么使⽤这两个图⽚,我们需要创建四个页⾯,如下所⽰:img2, img1, img2, img10, 1, 2, 3重要的⼀步:注意上⾯的索引编号,当滑动到第0 页时,即第0页的滚动事件(后⽂解释)结束后,将第0页变成第2页,由于第0页和第2页是⼀模⼀样的,所以视觉上感觉不到变化。
同理,当滚动到第3页(最后⼀页)时,我们将第3页换成第1页,由于第3页和第1页是⼀模⼀样的,所以视觉上感觉不到变化。
由此⽆限循环达成,即⼀旦移动到头部,就切换到倒数第⼆个。
⼀旦到最后⼀个,就切换到第⼆个索引。
页⾯翻页的实现思路我们需要了解viewPager的⼀些事件监听: addOnPageChangeListener(mOnPageChangeListener);这个⽅法添加⼀个监听器,⽤于监听页⾯改变。
它的监听器的⽅法有:void onPageSelected(int postion) 当页⾯被选中时发⽣。
Android自定义ViewGroup(二)——带悬停标题的ExpandableListView
Android自定义ViewGroup(二)——带悬停标题的ExpandableListView也就是说,在某一个分组内部滚动时,要求分组标题悬停,当滚出该分组范围时,把标题顶出去,悬停下一个分组的标题。
正好看到一个比较有趣的思路,做了一个实现,在这里分享一下。
代码结构如下,基本上是一个MVC的架构:既然是点击可收缩展开的列表,显然要用ExpandableListView,关于这个类的用法这里就不赘述了,网上一搜一大把,其实跟ListView的用法差不多,不过它帮你分了组,所以原来Adapter里的getView()就变成了getGroupView()和getChildView(),getCount()就变成了getGroupCount()等等。
另外既然要支持收缩展开,必然会提供collapseGroup()和expandGroup()等接口。
下面分析如何添加悬停标题,其实精华部分就一句话:悬停标题是画上去的,而不是加到view hierarchy里去,具体根据滚动的情况确定如何画。
首先我们来写一个DockingExapandableListView类,继承自ExpandableListView,包含一个View类型的成员变量mDockingHeader。
一、重写onMeasure()和onLayout()方法[java] view plain copy 在CODE上查看代码片派生到我的代码片@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);if (mDockingHeader != null) {measureChild(mDockingHeader, widthMeasureSpec, heightMeasureSpec);mDockingHeaderWidth = mDockingHeader.getMeasuredWidth();mDockingHeaderHeight = mDockingHeader.getMeasuredHeight();}}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {super.onLayout(changed, l, t, r, b);if (mDockingHeader != null) {yout(0, 0, mDockingHeaderWidth, mDockingHeaderHeight);}}这个比较简单,就是测量一下这个标题视图的宽度和高度。
用Toast显示自定义的view
用Toast显示自定义的view1.布局文件toast_view.xml具体的代码如下:<RelativeLayout xmlns:android="/apk/res/andr oid"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="com.shunchang.yingyong.test.cgq.MainActivity"android:background="#fff000"><TextViewandroid:id="@+id/title_tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/hello_world"android:layout_alignParentLeft="true"/><ScrollViewandroid:id="@+id/scrollView1"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_below="@+id/title_tv" ><LinearLayoutandroid:id="@+id/all_ll"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:id="@+id/name_tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/hello_world"/></LinearLayout></ScrollView></RelativeLayout>2.设置显示自定义view的代码:publicclass ToastUntils {publicstaticvoid showToast(Context mContext,String value){ View view=LayoutInflater.from(mContext).inflate(yout.toast_view, null);TextView tvTextView=(TextView)view.findViewById(_tv);tvTextView.setText(value);Toast toast=new Toast(mContext);toast.setView(view);toast.setGravity(Gravity.CENTER, 0, 0);toast.show();}}3.调用过程:ToastUntils.showToast(MainActivity.this,”要显示的内容”);4.Toast 源码解析:Toast 的构造防范如下public Toast(Context context) {mContext = context;mTN = new TN();mTN.mY = context.getResources().getDimensionPixelSize(com.android.internal.R.dimen.toast_y_offset);mTN.mGravity = context.getResources().getInteger(com.android.internal.R.integer.config_toastDefaultGravity);}Toast设置自定义view/*** Set the view to show.* @see #getView*/publicvoid setView(View view) {mNextView = view;}显示的位置/*** Set the location at which the notification should appear on the screen.* @see android.view.Gravity* @see #getGravity*/publicvoid setGravity(int gravity, int xOffset, int yOffset) {mTN.mGravity = gravity;mTN.mX = xOffset;mTN.mY = yOffset;}常见的toast调用的方法:/*** Make a standard toast that just contains a text view.** @param context The context to use. Usually your{@linkandroid.app.Application}* or{@link android.app.Activity} object.* @param text The text to show. Can be formatted text.* @param duration How long to display the message. Either {@link #LENGTH_SHORT} or* {@link #LENGTH_LONG}**/publicstatic Toast makeText(Context context, CharSequence text,@Duration int duration) {Toast result = new Toast(context);LayoutInflater inflate = (LayoutInflater)context.getSystemService(YOUT_INFLATER_SERVICE);View v =inflate.inflate(yout.transient_notification, null);TextView tv =(TextView)v.findViewById(com.android.internal.R.id.message);tv.setText(text);result.mNextView = v;result.mDuration = duration;return result;}/*** Make a standard toast that just contains a text view with the text from a resource.** @param context The context to use. Usually your{@linkandroid.app.Application}* or{@link android.app.Activity} object.* @param resIdThe resource id of the string resource to use. Can be formatted text.* @param duration How long to display the message. Either {@link #LENGTH_SHORT} or* {@link #LENGTH_LONG}** @throws Resources.NotFoundException if the resource can't be found. */publicstatic Toast makeText(Context context, @StringRes int resId,@Duration int duration)throws Resources.NotFoundException {return makeText(context, context.getResources().getText(resId), duration);}。
自定义View的流程和步骤
lineStikeWidth = a.getDimension(R.styleable.StepView_line_stroke_width, defaultLineStikeWidth);
textColor = a.getColor(R.styleable.StepView_text_color, defaultTextColor); textSize = a.getDimension(R.styleable.StepView_text_size, defaultTextSize); text2LineMargin = a.getDimension(R.styleable.StepView_text_to_line_margin, defaultText2DotMargin); margin = (int) a.getDimension(R.styleable.StepView_margin, defalutMargin); line2TopMargin = a.getDimension(R.styleable.StepView_line_to_top_margin, defaultLine2TopMargin); text2BottomMargin = a.getDimension(R.styleable.StepView_text_to_bottom_margin, defaultText2BottomMargin); clickable = a.getBoolean(R.styleable.StepView_is_view_clickable, defaultViewClickable); a.recycle(); //当文字在线条上面时,参数倒置 if (!isTextBelowLine) {
Android自定义view实现日历打卡签到
Android⾃定义view实现⽇历打卡签到本⽂实例为⼤家分享了Android⾃定义view实现⽇历打卡签到的具体代码,供⼤家参考,具体内容如下1.说明⾃⼰写⼀个view实现每天签到的功能,设置背景图⽚2.效果图3.主界⾯package com.example.myapplication30;import androidx.appcompat.app.AppCompatActivity;import android.annotation.SuppressLint;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.TextView;import java.util.ArrayList;import java.util.Calendar;import java.util.List;public class MainActivity extends AppCompatActivity {//参考⽹址:https:///MacaoPark/article/details/102069775private TextView mTvDaySum;private TextView mTvMonth;private SignView mCvCalendar;private List<SignEntity> data;private Calendar calendar;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(yout.activity_main);mTvDaySum = findViewById(R.id.punch_tv_day_sum);mTvMonth = findViewById(R.id.punch_tv_month);mCvCalendar = findViewById(R.id.punch_cv_calendar);}@Overrideprotected void onStart() {super.onStart();onReady();}@SuppressLint("SetTextI18n")private void onReady() {calendar = Calendar.getInstance();int year = calendar.get(Calendar.YEAR);int month = calendar.get(Calendar.MONTH);//int date = calendar.get(Calendar.DATE);int dayOfMonthToday = calendar.get(Calendar.DAY_OF_MONTH);List<SignDate> signDates = new ArrayList<>();signDates.add(new SignDate(2021, 5, 1, true));signDates.add(new SignDate(2021, 5, 2, true));signDates.add(new SignDate(2021, 5, 3, true));signDates.add(new SignDate(2021, 5, 4, true));signDates.add(new SignDate(2021, 5, 5, true));mTvDaySum.setText("本期连续登录\t"+signDates.size()+"\t天");mTvMonth.setText(year+"年"+getResources().getStringArray(R.array.month_array)[month]+"\t"+dayOfMonthToday+"⽇"); data = new ArrayList<>();for (int i = 1; i <= dayOfMonthToday; i++) {SignEntity signEntity = new SignEntity();if (i == dayOfMonthToday) {signEntity.setDayType(2);} else {signEntity.setDayType(1);}for (int j = 0; j < signDates.size(); j++) {if (signDates.get(j).getDay() == i) {signEntity.setDayType(0);break;} else if (dayOfMonthToday == i) {signEntity.setDayType(2);} else {signEntity.setDayType(1);}}data.add(signEntity);}SignAdapter signAdapter = new SignAdapter(data);mCvCalendar.setAdapter(signAdapter);}}4.适配器package com.example.myapplication30;import java.util.List;/*** SignAdapter* Created by E.M on 2016/4/21.*/public class SignAdapter extends CalendarAdapter {private List<SignEntity> data;public SignAdapter(List<SignEntity> data) {this.data = data;}@Overridepublic SignView.DayType getType(int dayOfMonth) {return SignView.DayType.valueOf(data.get(dayOfMonth - 1).getDayType());}}5.⾃定义viewpackage com.example.myapplication30;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.BitmapShader;import android.graphics.Canvas;import android.graphics.Color;import poseShader;import android.graphics.LinearGradient;import android.graphics.Paint;import android.graphics.Path;import android.graphics.PorterDuff;import android.graphics.RadialGradient;import android.graphics.Rect;import android.graphics.Shader;import android.graphics.SweepGradient;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import java.util.Calendar;/*** 签到⽇历控件* Created by E.M on 2016/4/20.*/public class SignView extends View {private static final String[] WEEK_MARK = {"⼀", "⼆", "三", "四", "五", "六", "⽇"}; private static final int MAX_COLUMN = 7;/*** 周内*/private static final int COLOR_MARKER_WEEKDAY = 0xFF999999;private static final int COLOR_MARKER_WEEKEND = 0xFF1B89CD;/*** 已签到背景⾊*///private static final int COLOR_BACKGROUND_HIGHLIGHT = 0xFFFF0000;/*** 未签到背景⾊*/private static final int COLOR_BACKGROUND_NORMAL = 0xFF9C9C9C;/*** 等待签到背景⾊*/private static final int COLOR_BACKGROUND_WAIT = 0xFFFE7471;/*** 已签到⽂字颜⾊*/private static final int COLOR_TEXT_HIGHLIGHT = 0xFFFFFFFF;/*** 未签到⽂字颜⾊*/private static final int COLOR_TEXT_NORMAL = 0xFF606060;// /**// * 不可⽤⽂字颜⾊// */// private static final int COLOR_TEXT_DISABLED = 0xFFD4D4D4;private static final int MARKER_TEXT_SIZE = 40;private static final int CELL_TEXT_SIZE = 40;private static final int VERTICAL_SPACE = 51;private static final int VERTICAL_MARGIN = 62;private static final int HORIZONTAL_MARGIN = 39;private static final int CELL_SIZE = 80;private static final int WAIT_LINE_SIZE = 14;private int dayOfMonthToday;private int markerTextY;private int verticalCellTop;private int sumDayOfMonth;private int daysOfFirstWeek;private int horizontalSpace;private int deltaTextCellY;private int deltaTextMarkerY;private int verticalSpace;private int verticalMargin;private int horizontalMargin;private int cellSize;private int waitLineSize;private Path waitPath;private Rect waitRect;private Paint paintWeekday;private Paint paintWeekend;private Paint paintTextNormal;private Paint paintTextHighlight;private Paint paintBackgroundWait;private Paint paintBackgroundNormal;private Paint paintBackgroundHighlight;private CalendarAdapter adapter;public SignView(Context context) {this(context, null);}public SignView(Context context, AttributeSet attrs) {this(context, attrs, -1);}public SignView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initResolution();initPaint();initData();}private void initResolution() {// resolutionUtil = ResolutionUtil.getInstance();// verticalSpace = resolutionUtil.formatVertical(VERTICAL_SPACE);// verticalMargin = resolutionUtil.formatVertical(VERTICAL_MARGIN);// horizontalMargin = resolutionUtil.formatHorizontal(HORIZONTAL_MARGIN); // cellSize = resolutionUtil.formatVertical(CELL_SIZE);// waitLineSize = resolutionUtil.formatVertical(WAIT_LINE_SIZE);verticalSpace = VERTICAL_SPACE;verticalMargin = VERTICAL_MARGIN;horizontalMargin = HORIZONTAL_MARGIN;cellSize = CELL_SIZE;waitLineSize = WAIT_LINE_SIZE;}private void initPaint() {// int markerTextSize = resolutionUtil.formatVertical(MARKER_TEXT_SIZE); // int cellTextSize = resolutionUtil.formatVertical(CELL_TEXT_SIZE);int markerTextSize = MARKER_TEXT_SIZE;int cellTextSize = CELL_TEXT_SIZE;paintWeekday = new Paint();paintWeekday.setAntiAlias(true);paintWeekday.setColor(COLOR_MARKER_WEEKDAY);paintWeekday.setTextSize(markerTextSize);paintWeekday.setTextAlign(Paint.Align.CENTER);paintWeekend = new Paint();paintWeekend.setAntiAlias(true);paintWeekend.setColor(COLOR_MARKER_WEEKEND);paintWeekend.setTextSize(markerTextSize);paintWeekend.setTextAlign(Paint.Align.CENTER);paintTextNormal = new Paint();paintTextNormal.setAntiAlias(true);paintTextNormal.setColor(COLOR_TEXT_NORMAL);paintTextNormal.setTextSize(cellTextSize);paintTextNormal.setTextAlign(Paint.Align.CENTER);paintTextHighlight = new Paint();paintTextHighlight.setAntiAlias(true);paintTextHighlight.setColor(COLOR_TEXT_HIGHLIGHT);paintTextHighlight.setTextSize(cellTextSize);paintTextHighlight.setTextAlign(Paint.Align.CENTER);paintBackgroundWait = new Paint();paintBackgroundWait.setAntiAlias(true);paintBackgroundWait.setColor(COLOR_BACKGROUND_WAIT);paintBackgroundWait.setStrokeWidth(2);paintBackgroundWait.setStyle(Paint.Style.STROKE);paintBackgroundNormal = new Paint();paintBackgroundNormal.setAntiAlias(true);paintBackgroundNormal.setColor(COLOR_BACKGROUND_NORMAL);paintBackgroundNormal.setStrokeWidth(2);paintBackgroundNormal.setStyle(Paint.Style.STROKE);paintBackgroundHighlight = new Paint();paintBackgroundHighlight.setAntiAlias(true);paintBackgroundHighlight.setStrokeWidth(2);paintBackgroundHighlight.setStyle(Paint.Style.FILL);//颜⾊//paintBackgroundHighlight.setColor(COLOR_BACKGROUND_HIGHLIGHT);//多种颜⾊数组//int[] colors = {Color.RED,Color.GREEN,Color.BLUE,Color.YELLOW,Color.MAGENTA};//float[] position = {0f, 0.2f, 0.4f, 0.6f, 1.0f};//Shader shader1 = new LinearGradient(100,850,600,850,colors,position,Shader.TileMode.CLAMP);//paintBackgroundHighlight.setShader(shader1);//设置背景图⽚/* Bitmap placeholder = BitmapFactory.decodeResource(getResources(), R.mipmap.small);Shader shader1 = new BitmapShader(placeholder, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); paintBackgroundHighlight.setShader(shader1);*/}private void initData() {Paint.FontMetricsInt fmiMarker = paintWeekday.getFontMetricsInt();deltaTextMarkerY = -(fmiMarker.bottom - fmiMarker.top) / 2 - fmiMarker.top;markerTextY = verticalMargin + cellSize / 2;Paint.FontMetricsInt fmiCell = paintTextNormal.getFontMetricsInt();deltaTextCellY = -(fmiCell.bottom - fmiCell.top) / 2 - fmiCell.top;verticalCellTop = verticalMargin + cellSize;Calendar calendarToday = Calendar.getInstance();dayOfMonthToday = calendarToday.get(Calendar.DAY_OF_MONTH);int dayOfWeek;sumDayOfMonth = calendarToday.getActualMaximum(Calendar.DAY_OF_MONTH);Calendar calendarFirstDay = Calendar.getInstance();calendarFirstDay.set(Calendar.DAY_OF_MONTH, 1);dayOfWeek = calendarFirstDay.get(Calendar.DAY_OF_WEEK);if (dayOfWeek == Calendar.SUNDAY) {dayOfWeek = 7;} else {dayOfWeek = dayOfWeek - 1;}daysOfFirstWeek = MAX_COLUMN - dayOfWeek + 1;}private void createWaitBackground(int topX, int topY) {waitPath = new Path();waitPath.moveTo(topX, topY + waitLineSize);waitPath.lineTo(topX, topY);waitPath.lineTo(topX + waitLineSize, topY);waitPath.moveTo(topX + cellSize - waitLineSize, topY + cellSize);waitPath.lineTo(topX + cellSize, topY + cellSize);waitPath.lineTo(topX + cellSize, topY + cellSize - waitLineSize);waitRect = new Rect(topX, topY, topX + cellSize, topY + cellSize);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);horizontalSpace = (w - MAX_COLUMN * cellSize - horizontalMargin * 2) / (MAX_COLUMN - 1);}@Overridepublic void draw(Canvas canvas) {super.draw(canvas);drawWeekMark(canvas);drawCellsBackground(canvas);drawCells(canvas);}private void drawWeekMark(Canvas canvas) {int y = markerTextY + deltaTextMarkerY;for (int i = 0; i < 7; i++) {int x = horizontalMargin + i * (horizontalSpace + cellSize)+ cellSize / 2;if (i < 5) {canvas.drawText(WEEK_MARK[i], x, y, paintWeekday);} else {canvas.drawText(WEEK_MARK[i], x, y, paintWeekend);}}}private void drawCellsBackground(Canvas canvas) {for (int i = 1; i <= dayOfMonthToday; i++) {drawCellBackground(canvas, i, getColumnIndex(i), getRowIndex(i));}}/*** 根据⾏列序号绘制⽇期背景** @param canvas 画布* @param dayOfMonth ⽇期* @param column 列序号* @param row ⾏序号*/private void drawCellBackground(Canvas canvas, int dayOfMonth, int column, int row) {int x = horizontalMargin + column * (horizontalSpace + cellSize)+ cellSize / 2;int y = verticalCellTop + verticalSpace * (row + 1) + cellSize * row + cellSize / 2;if (adapter != null) {DayType dayType = adapter.getType(dayOfMonth);switch (dayType) {case WAITING:if (waitPath == null) {createWaitBackground(x - cellSize / 2, y - cellSize / 2);}canvas.drawPath(waitPath, paintBackgroundWait);break;case SIGNED:// canvas.drawCircle(x, y, cellSize/2, paintBackgroundHighlight);// canvas.drawRect(x - 60, y - 60, x + 60, y + 60, paintBackgroundHighlight);// 正⽅形// Bitmap placeholder = BitmapFactory.decodeResource(getResources(), R.mipmap.purtest);// canvas.drawBitmap(placeholder,);wCircle(x, y, cellSize/2, paintBackgroundHighlight);canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.small3),x-40, y-40 , paintBackgroundHighlight); break;default:canvas.drawCircle(x, y, cellSize / 2, paintBackgroundNormal);break;}} else {canvas.drawCircle(x, y, cellSize / 2, paintBackgroundNormal);}}private void drawCells(Canvas canvas) {for (int i = 1; i <= sumDayOfMonth; i++) {drawCell(canvas, i, getColumnIndex(i), getRowIndex(i));}}/*** 根据⾏列序号绘制⽇期** @param canvas 画布* @param dayOfMonth ⽇期* @param column 列序号* @param row ⾏序号*/private void drawCell(Canvas canvas, int dayOfMonth, int column, int row) {int x = horizontalMargin + column * (horizontalSpace + cellSize)+ cellSize / 2;int y = verticalCellTop + verticalSpace * (row + 1) + cellSize * row + cellSize / 2+ deltaTextCellY;if (adapter != null && dayOfMonth <= dayOfMonthToday) {DayType dayType = adapter.getType(dayOfMonth);Paint paint;switch (dayType) {case SIGNED:paint = paintTextHighlight;break;default:paint = paintTextNormal;break;}canvas.drawText(String.valueOf(dayOfMonth), x, y, paint);} else {canvas.drawText(String.valueOf(dayOfMonth), x, y, paintTextNormal);}}/*** 获取列序号** @param dayOfMonth ⽇期* @return 列序号*/private int getColumnIndex(int dayOfMonth) {Calendar calendar = Calendar.getInstance();calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);if (dayOfWeek == Calendar.SUNDAY) {dayOfWeek = 6;} else {dayOfWeek = dayOfWeek - 2;}return dayOfWeek;}/*** 获取⾏序号** @param dayOfMonth ⽇期* @return ⾏序号*/private int getRowIndex(int dayOfMonth) {float weight = (dayOfMonth - daysOfFirstWeek) / (MAX_COLUMN * 1f);double rowIndexDouble = Math.abs(Math.ceil(weight));return (int) rowIndexDouble;}@Overridepublic boolean onTouchEvent(MotionEvent event) {if (event.getAction() == MotionEvent.ACTION_UP) {float x = event.getX();float y = event.getY();if (waitPath != null) {if (adapter.getType(dayOfMonthToday).equals(DayType.WAITING)) {if (x >= waitRect.left && y >= waitRect.top && x <= waitRect.right && y <= waitRect.bottom) { if (onTodayClickListener != null) {onTodayClickListener.onTodayClick();}}}}}return true;}public void setAdapter(CalendarAdapter adapter) {this.adapter = adapter;this.invalidate();}public int getDayOfMonthToday() {return dayOfMonthToday;}public void notifyDataSetChanged() {invalidate();}private OnTodayClickListener onTodayClickListener;public void setOnTodayClickListener(OnTodayClickListener onTodayClickListener) {this.onTodayClickListener = onTodayClickListener;}public interface OnTodayClickListener {void onTodayClick();}public enum DayType {/*** 已签到状态,时间已过*/SIGNED(0),/*** 未签到状态,时间已过*/UNSIGNED(1),/*** 等待状态,即当⽇还未签到*/WAITING(2),/*** 不可达到状态,未到时间*/UNREACHABLE(3),/*** 不可⽤状态,⾮当前⽉份*/DISABLED(4);private int value;DayType(int value) {this.value = value;}public int getValue() {return value;}public static DayType valueOf(int value) {switch (value) {case 0:return SIGNED;case 1:return UNSIGNED;case 2:return WAITING;case 3:return UNREACHABLE;case 4:return DISABLED;default:return DISABLED;}}}}以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
Android自定义view中实现LifecycleOwner
Android自定义view中实现LifecycleOwner Android提供了一个强大的架构组件,Lifecycle,用于帮助开发者在应用程序的不同组件(如Activity、Fragment、Service等)生命周期发生变化时执行相应的操作。
然而,在自定义View中使用Lifecycle组件并不是那么直接。
在本文中,将介绍如何在自定义View中实现LifecycleOwner接口。
LifecycleOwner接口是Lifecycle组件的核心接口之一,它提供了一个Lifecycle对象,用于观察其生命周期状态。
在Android中,Activity和Fragment实现了LifecycleOwner接口,并且它们的生命周期状态可以直接通过lifecycle对象获取。
但是,自定义View并没有直接实现该接口,这意味着我们需要自己实现它。
要在自定义View中实现LifecycleOwner接口,我们首先需要创建一个LifecycleRegistry对象,并在相应的生命周期方法中更新其状态。
在自定义View的构造方法中创建一个LifecycleRegistry对象,并在构造方法中调用init方法进行初始化。
```kotlinprivate val lifecycleRegistry: LifecycleRegistryconstructor(context: Context) : this(context, null)constructor(context: Context, attrs: AttributeSet?) :super(context, attrs)lifecycleRegistry = LifecycleRegistry(this)lifecycleRegistry.currentState = Lifecycle.State.INITIALIZED}private fun ini//进行一些初始化操作lifecycleRegistry.currentState = Lifecycle.State.CREATED }override fun onAttachedToWindosuper.onAttachedToWindowlifecycleRegistry.currentState = Lifecycle.State.STARTED }override fun onDetachedFromWindolifecycleRegistry.currentState = Lifecycle.State.DESTROYED super.onDetachedFromWindow}override fun onStarsuper.onStartlifecycleRegistry.currentState = Lifecycle.State.STARTED }override fun onStolifecycleRegistry.currentState = Lifecycle.State.CREATEDsuper.onStop}override fun getLifecycle(: Lifecyclereturn lifecycleRegistry}```在上述代码中,我们创建了一个LifecycleRegistry对象,并在构造方法和相应的生命周期方法中更新其状态。
自定义view的基本流程
自定义view的基本流程1.自定义View的意义自定义View是开发Android应用的常见需求,它可以实现各种特殊的UI效果和交互效果。
有时候,自带的一些View无法满足我们的需求,而自定义一个View可以帮助我们实现想要的效果。
2.自定义View的基本流程要实现一个自定义View,通常需要经过以下基本流程:2.1创建自定义View类首先,我们需要创建一个自定义View的类,并继承自View或其子类,比如ImageView、TextView等。
在类中,我们可以重写onMeasure()、onLayout()和onDraw()等方法来实现自定义View的特殊行为。
2.2声明自定义View的属性如果我们希望在XML布局文件中使用自定义View,并可以在代码中设置属性值,就需要在类中声明自定义属性。
这可以通过在res/values/attrs.xml文件中定义属性集合来实现。
2.3实现自定义View的布局在onMeasure()方法中,我们可以指定自定义View在测量时占用的空间大小。
在onLayout()方法中,我们可以指定子View的位置。
2.4实现自定义View的绘制在onDraw()方法中,我们可以通过调用Canvas的API来实现自定义View的绘制效果。
例如,我们可以通过Path绘制自定义的图形,通过Paint设置绘制时的样式和颜色等。
2.5对自定义View进行测试最后,我们需要通过手动测试、单元测试、自动化UI测试等方式,对自定义View进行测试和验证。
3.自定义View的实例以下是一个简单的自定义View实例,用于显示一个可以按下和弹起的按钮:```public class MyButton extends View{private boolean isPressed=false;public MyButton(Context context){super(context);}public MyButton(Context context,AttributeSet attrs) {super(context,attrs);}@Overrideprotected void onDraw(Canvas canvas){super.onDraw(canvas);Paint paint=new Paint();paint.setColor(isPressed?Color.RED:Color.GREEN);canvas.drawRect(0,0,getWidth(),getHeight(), paint);}@Overridepublic boolean onTouchEvent(MotionEvent event){if(event.getAction()==MotionEvent.ACTION_DOWN) {isPressed=true;invalidate();return true;}else if(event.getAction()==MotionEvent.ACTION_UP){isPressed=false;invalidate();return true;}return super.onTouchEvent(event);}}```以上代码中,我们实现了一个MyButton类,当MyButton被按下时,它的背景颜色会变成红色,当MyButton被弹起时,它的背景颜色会回到绿色。
【朝花夕拾】Android自定义View篇之(四)自定义View的三种实现方式及自定义属性使用介绍
【朝花⼣拾】Android⾃定义View篇之(四)⾃定义View的三种实现⽅式及⾃定义属性使⽤介绍前⾔尽管Android系统提供了不少控件,但是有很多酷炫效果仍然是系统原⽣控件⽆法实现的。
好在Android允许⾃定义控件,来弥补原⽣控件的不⾜。
但是在很多初学者看来,⾃定义View似乎很难掌握。
其中有很⼤⼀部分原因是我们平时看到的⾃定义View使⽤中,有多种形式,有的寥寥数笔,有的逻辑很复杂,有的直接继承View或ViewGroup,有的却直接继承系统的原⽣控件,有的可以直接使⽤系统定义的属性,⽽有的却⾃定义了⾃⼰的属性......所以不明⽩使⽤规则的开发者,很容易被这只“纸⽼虎”吓到。
实际上实现⾃定义View的⽅式,从整体上看,只分为三种:组合控件,继承控件,⾃绘控件。
然后就是根据需要来添加⾃定义的属性,就这么简单。
本⽂将会针对这4个⽅⾯进⾏详细的讲解。
主要内容如下:⼀、组合控件组合控件,顾名思义,就是将系统原有的控件进⾏组合,构成⼀个新的控件。
这种⽅式下,不需要开发者⾃⼰去绘制图上显⽰的内容,也不需要开发者重写onMeasure,onLayout,onDraw⽅法来实现测量、布局以及draw流程。
所以,在实现⾃定义view的三种⽅式中,这⼀种相对⽐较简单。
实际开发中,标题栏就是⼀个⽐较常见的例⼦。
因为在⼀个app的各个界⾯中,标题栏基本上是⼤同⼩异,复⽤率很⾼。
所以经常会将标题栏单独做成⼀个⾃定义view,在不同的界⾯直接引⼊即可,⽽不⽤每次都把标题栏布局⼀遍。
本节就⾃定义⼀个标题栏,包含标题和返回按钮两个控件,来介绍这种组合控件的实现⽅式。
1、定义标题栏布局⽂件定义标题栏的布局⽂件custom_title_view.xml,将返回按钮和标题⽂本进⾏组合。
这⼀步⽤于确定标题栏的样⼦,代码如下所⽰:1<?xml version="1.0" encoding="utf-8"?>2<RelativeLayout xmlns:android="/apk/res/android"3 android:layout_width="match_parent"4 android:layout_height="wrap_content"5 android:background="@android:color/holo_orange_light">6<Button7android:id="@+id/btn_left"8 android:layout_width="wrap_content"9 android:layout_height="wrap_content"10 android:layout_centerVertical="true"11 android:layout_marginLeft="5dp"12 android:text="Back"13 android:textColor="@android:color/white"/>1415<TextView16android:id="@+id/title_tv"17 android:layout_width="wrap_content"18 android:layout_height="wrap_content"19 android:layout_centerInParent="true"20 android:text="Title"21 android:textColor="@android:color/white"22 android:textSize="20sp"/>23</RelativeLayout>这个布局很简单,就不多说了。
View与ViewGroup有什么区别?
View与ViewGroup有什么区别?Android的UI界⾯都是由View和ViewGroup及其派⽣类组合⽽成的。
其中,View是所有UI组件的基类,⽽ ViewGroup是容纳这些组件的容器,其本⾝也是从View派⽣出来的. View对象是Android平台中⽤户界⾯体现的基础单位。
View类是它称为“widgets(⼯具)”的⼦类的基础,它们提供了诸如⽂本输⼊框和按钮之类的UI对象的完整实现。
ViewGroup类同样为其被称为“Layouts(布局)”的⼦类奠定了基础,它们提供了象流式布局、表格布局以及相对布局之类的布局架构。
⼀般来说,开发Android应⽤程序的UI界⾯都不会直接使⽤View和ViewGroup,⽽是使⽤这两⼤基类的派⽣类。
View派⽣出的直接⼦类有:AnalogClock,ImageView,KeyboardView, ProgressBar,SurfaceView, TextView,ViewGroup,ViewStub View派⽣出的间接⼦类有:AbsListView,AbsSeekBar, AbsSpinner, AbsoluteLayout,AdapterView,AdapterViewAnimator, AdapterViewFlipper, AppWidgetHostView,AutoCompleteTextView,Button,CalendarView, CheckBox, CheckedTextView, Chronometer, CompoundButton, ViewGroup派⽣出的直接⼦类有:AbsoluteLayout,AdapterView,FragmentBreadCrumbs,FrameLayout, LinearLayout,RelativeLayout,SlidingDrawer ViewGroup派⽣出的间接⼦类有:AbsListView,AbsSpinner, AdapterViewAnimator, AdapterViewFlipper, AppWidgetHostView, CalendarView, DatePicker, DialerFilter, ExpandableListView, Gallery, GestureOverlayView,GridView,HorizontalScrollView, ImageSwitcher,ListView, 这⾥特别指出,ImageView是布局具有图⽚效果的UI常⽤的类,SurfaceView是⽤来进⾏游戏开发的与⼀般View 相⽐较为特殊的⾮常重要的类,⽽AbsoluteLayout、 FrameLayout,LinearLayout, RelativeLayout这⼏个ViewGroup 的直接⼦类是Android UI布局中最基本的布局元素。
Android自定义View实现绘制虚线的方法详解
Android⾃定义View实现绘制虚线的⽅法详解前⾔说实话当第⼀次看到这个需求的时候,第⼀反应就是Canvas只有drawLine⽅法,并没有drawDashLine⽅法啊!这咋整啊,难道要我⾃⼰做个遍历不断的drawLine?不到1秒,我就放弃这个想法了,因为太恶⼼了。
⽅法肯定是有的,只不过我不知道⽽已。
绘制⽅法最简单的⽅法是利⽤ShapeDrawable,⽐如说你想⽤虚线要隔开两个控件,就可以在这两个控件中加个View,然后给它个虚线背景。
嗯,理论上就是这样⼦的,实现上也很简单。
<!-- drawable ⽂件 --><?xml version="1.0" encoding="utf-8"?><shape xmlns:android="/apk/res/android"android:shape="line"><strokeandroid:width="1dp"android:color="@color/dash_line"android:dashGap="2dp"android:dashWidth="3dp"/></shape><!-- 布局⽂件 --><?xml version="1.0" encoding="utf-8"?><LinearLayoutxmlns:android="/apk/res/android"xmlns:tools="/tools"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"android:gravity="center"tools:context="hope.example.dashlinedemo.MainActivity"><TextViewandroid:layout_width="match_parent"android:layout_height="40dp"android:gravity="center"android:text="分享给微信好友"/><Viewandroid:layout_width="match_parent"android:layout_height="2dp"android:background="@drawable/dash_line" /><TextViewandroid:layout_width="match_parent"android:layout_height="40dp"android:gravity="center"android:text="分享⾄朋友圈" /></LinearLayout>写完之后,从Android Studio的预览功能上就可以看到效果了。
【推荐下载】Android NDK:从源代码编译自定义WebView
Android NDK:从源代码编译自定义WebViewAndroid NDK:从源代码编译自定义WebView[英]Android NDK: custom WebView compiling from source My goal is to create a modified version of WebView (call it WebViewCustom) in Android for my personal use in my application. WebView is WebKit based so I need to compile it in C by means of NDK. I am not interested in participating in the open-source Android project now, so my need is just to be able to compile the original C source of WebView and use it as a customized control in my Android application (both SDK and NDK are possible environments for the application). I think it is possible without all that GIT WebKit stuff I am not interested in, but I am not an expert of the Android and WebKit projects. Is it possible to just edit and compile the WebView code and put it in my application as a new control? Can you show me the mainsteps of this task?我的目标是在Android 中创建一个WebView 的修改版本(称之为WebViewCustom),供我个人在我的应用程序中使用。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
上面是自定义控件的实现,下面将要说的是组合控件的实现。在组合控件中,最经常用到的应该就是RadioGroup和RadioButton。RadioButton的实现已经在上面介绍了。下面要介绍RadioGroup的自定义控件和功能扩展:
代码如下:
android:text="@string/yes" android:textSize="18sp">
</org.kandy.view.RadioButton>
同样,红色部分可以先不看,也不需要加入到代码中,这个时候加入会报错,请注意。
3、attrs.xml属性定义。
在我们的思想中,既然我在自定义控件中加入了一个新的属性,那么我就应该能够在xml中引用它,并对它赋初始值。我当初也是这样想的。可是却无从下手。就是这一点,折腾了我一个下午。
方法非常简单,循环或者RadioGroup的子控件,检测哪个控件被checked,然后getValue,将此value赋值给RadioGroup的扩展属性value。在这里不多说了。相信大家都能看懂。
}
public RadioGroup(Context context) {
super(context);
}
//设置子控件的值
public void setChildValue(){
int n = this.getChildCount();
for(int i=0;i<n;i++){
final RadioButton radio = (RadioButton)this.getChildAt(i);
super(context, attrs, defStyle);
}
public String getValue() {
return this.mValue;
}
public void setValue(String value) {
this.mValue = value;
}
public RadioButton(Context context, AttributeSet attrs) {
this.mValue = value;
setChildValue();
}
public String getValue(){
getChildValue();
return this.mValue;
}
}
RadioGroup只做两件事:获取子控件(RadioButton)所选择的值;设置子控件要选择的值。
</declare-styleable>
</resources>
如果res下没有错误的话,在R中应该就会生成这些资源的id。这样我们就能在自定义控件中引用他们。
这里我们可能对format不是很熟悉,目前Android系统内置的格式类型有integer比如ProgressBar的进度值,float比如RatingBar的值可能是3.5颗星,boolean比如ToggleButton的是否勾选,string比如TextView的text属性,当然除了我们常见的基础类型外,Android的属性还有特殊的比如color是用于颜色属性的,可以识别为#FF0000等类型,当然还有dimension的尺寸类型,比如23dip,15px,18sp的长度单位,还有一种特殊的为reference,一般用于引用@+id/cwj @drawable/xxx这样的类型。
android:text="@string/yes" android:textSize="18sp">
</org.kandy.view.RadioButton>
</ScrollView>
红色部分首先声明命名空间。命名空间为fsms.路径是/apk/res/这一部分是不变的,后面接的是R的路径:org.kandy.R。然后在自定义控件的xml描述中就可以这样使用fsms:value="true"。这样就实现了自定义控件的初始化赋值。
a.recycle();
TypedArray其实就是一个存放资源的Array,首先从上下文中获取到R.styleable.RadioButton这个属性资源的资源数组。attrs是构造函数传进来,应该就是对应attrs.xml文件。a.getString(R.styleable.RadioButton_value);这句代码就是获取attrs.xml中定义的属性,并将这个属性的值传给本控件的mValue.最后,返回一个绑定结束的信号给资源:a.recycle();绑定结束。
if(radio.getValue().equals(this.mValue)){
radio.setChecked(true);
}else{
radio.setChecked(false);
}
}
}
//获取子类的值
public void getChildValue(){
int n = this.getChildCount();
/**
*跟values/attrs.xml里面定义的属性绑定
*/
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.RadioButton);
this.mValue = a.getString(R.styleable.RadioButton_value);
public class RadioGroup extends android.widget.RadioGroup {
private String mValue;
public RadioGroup(Context context, AttributeSet attrs) {
super(context, attrs);
当然什么时候用reference呢?我们就以定义一个颜色为例子,
<attr name="red" format="color|reference" />这里我们用了逻辑或的运算符,定义的红色是颜色类型的,同时可以被引用。
4、控件属性与XML定义绑定。
这下子我们又回到了自定义控件的编写上来了。先看看我们在第一点提到的红色字体部分。这一部分就是实现控件属性与XML定义绑定的代码。
a.recycle();
} catch (Exception e) {
e.printStackTrace();
}
}
public RadioButton(Context context) {
super(context);
}
}
红色代码可以先不看。先看我们新加入的属性value,由于Android习惯属性命名以m开头。所以我们自定义控件就按照这个规则来写。不过对于setter、getter方法来说,不需要加入m。像上面的:属性名称mValue,setter:setValue(),getter:getValue()。当然,你也可以不按照Android的习惯来命名。
Android自定义控件的实现
可能是一直都在做Web的富客户端开发的缘故吧,在接触Android之后,发现其控件实在惨不忍睹(不知道是否说得过于偏激),我所说的惨不忍睹的意思不是说控件难看,Android的控件非常漂亮,这是我们公司公认的,但是最大的缺点在于控件功能非常弱小。弱小得一个Radio只能放一个text,而没有value(key)可以存放。这就是为什么我说惨不忍睹的原因。
但是这不能怪google,毕竟才刚刚发展起来,Android提供的只是一个最基本的控件实现,而非一个完整、强大的实现。可幸的是,Android提供了自定义控件的实现。有了自定义控件,我们就可以再Android的基础控件上实现我们想要的功能了。经过一天的摸索,我终于实现了我第一个自定义的组合控件——RadioButton组合RadioGroup!
super(context, attrs);
try {
/**
*跟values/attrs.xml里面定义的属性绑定
*/
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.RadioButton);
this.mValue = a.getString(R.styleable.RadioButton_value);
下面我将带领大家进入Android自定义控件的世界。如果觉得我的文章能够帮助大家的话,请大方留下你的一些话语。因为你们的留言是我分享经验的精神源泉!谢谢!
1、设置自定义控件:Android自带的RadioButton只能存放text,这不符合我们的需求,我们需要一个可以同时存放key-value对应的键值。所以我们要编写一个自定义控件能存放key-value。
public class RadioButton extends android.widget.RadioButton {
private String mValue;
public RadioButton(Context context, AttributeSet attrs, int defStyle) {
xmlns:fsms=/apk/res/org.kandy>
<org.kandy.view.RadioButton android:id="@id/isPayDepositTrue"fsms:value="true"
android:layout_width="wrap_content" android:layout_height="wrap_content"