Android进阶——自定义View之自己绘
制彩虹圆环调色板
引言
前面几篇文章都是关于通过继承系统View和组合现有View来实现自定义View的,刚好由于项目需要实现一个滑动切换LED彩灯颜色的功能,所以需要一个类似调色板的功能,随着手在调色板有效区域滑动,LED彩灯随即显示相应的颜色,也可以通过左右的按钮,按顺序切换显示一组颜色,同时都随着亮度的改变LED彩灯的亮度随即变化,这篇基本上把继承View重绘实现自定义控件的大部分知识总结了下(当然还有蛮多没有涉及到,比如说自适应布局等),源码在Github上
一、继承View绘制自定义控件的通用步骤
自定义属性和继承View重写onDraw方法
实现构造方法,其中public RainbowPalette(Context context, AttributeSet attrs) 必须实现,否则无法通过xml引用,public RainbowPalette(Context context) ,public RainbowPalette(Context context, AttributeSet attrs, int defStyleAttr)可选,通常在构造方法中完成属性和其他成员变量的初始化
重写onMeasure方法,否则在xml中有些设置布局参数无效
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(width, height);//重新设置View的位置,若不重写的话,则不会布局,即使设置centerInParent为true也无效
//setMeasuredDimension(width,height);
}
手动调用invalidate或者postInvalidateon方法完成界面刷新
重写onTouchEvent方法实现基本的交互
定义回调接口供外部调用
二、彩虹圆环调色板设计思想
1、UI构成
首先从整个形状来看是个圆环,系统自有的控件明显没具有这个功能,只能是继承View重写OnDraw来完成绘制工作。前面Android入门——利用Canvas完成绘制点、圆、直线、路径、椭圆、多边形等2D图形可以知道通过Paint可以在Canvas绘制任何图形,包括圆环,于是整体的结构就出来了,中心实体小圆作为指示当前颜色的标记,外圈渐变色圆环作为调色板的取色区域(可以通过给Paint传入Shader shader = new SweepGradient(0, 0, gradientColors, null)来绘制渐变色),最外圈的光环可以绘制多个圆环,而作为指示器标记的小圆点(也可以传入图片资源)也是一个圆形,如此一来UI方面的结构基本明了。
2、交互设计
一般来说自定义View的人机交互都是通过回调的方式的来实现的。
滑动选择颜色:自定义控件的滑动自然是重写onTouchEvent,然后调用invalidate手动触发View重绘(即重新调用onDraw)
颜色指示器的显示的位置:手动触发invalidate重新调用onDraw绘制
中心圆形自动同步选中的颜色:手动触发invalidate重新调用onDraw绘制
仅在圆环处滑动选择颜色:如果面积小于外圆大于内圆的就认为是有效滑动
/**
* 是否是有效Touch即是否是按在圆环之上的
* @return
*/
private boolean isEfectiveTouch(float x, float y, float outRadius, float inRadius){ double outCircle = Math.PI * outRadius * outRadius;
double inCircle = Math.PI * inRadius * inRadius;
double fingerCircle = Math.PI * (x * x + y * y);
if(fingerCircle < outCircle && fingerCircle > inCircle) {
return true;
}else {
return false;
}
}
三、实现彩虹圆环调色板
1、自定义属性
attr.xml
2、重写View
import com.xiaoi.app.zkSmartHome.R;
/**
* auther: Crazy.Mo
* Date: 2016/12/13
* Time:10:27
* Des:自定义的调色板,可以绘制圆形指示器,也可以自定义图标指示器,但是指示器会超出边界
*/
public class RainbowPalette extends View {
private Context context;
private Paint borderPaint;//色环外圈的彩虹圆环
private Paint palettePaint;//渐变色环的画笔
private Paint centerPaint;//中间圆画笔,用于显示当前选中的颜色
private Paint indictorPaint; // 可移动小球画笔
private int indictorColor;
private int[] gradientColors;//渐变色环颜色
private int centerCircleColor;
private int width;//当前调色板的宽度
private int height;//当前调色板的高度
private float paletteRadius;//色环半径,整个环形外径,直接决定这个调色板的整体的大小,画渐变环可以看成画一个完整的圆形再挖掉一个小圆
private float centerRadius;//中心圆半径
private float paletteWidth;//色环的宽度
private float indictorRadius;//小球的半径
private Point indictorPosition;// 小球当前位置
private Point centerPosition;//圆心的位置,色环圆心的位置
private Bitmap indicatorBitmap; // 指示器小球的图标
private int indictorResId;//指示器图标的id
private RainbowPalette.OnColorChangedListen listen;
private static boolean isShowIndictor=true;
private final static int BORDER_WIDTH=2;
private final static int PALETTE_WIDTH=100;
private final static int CENTER_CIRCLE_WIDTH=5;
private final static int INDICTOR_WIDTH=5;
private final static int DEF_INDICTOR_COLOR=0xFFc9f5f1;//设置指示小圆形的颜色private final static int DEF_CIRCLE_COLOR=0xFF0511FB;//设置中间圆的默认颜色
public RainbowPalette(Context context) {
super(context);
}
public RainbowPalette(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
init(attrs);
}
public RainbowPalette(Context context, AttributeSet attrs, int defStyleAttr){
super(context, attrs, defStyleAttr);
this.context=context;
init(attrs);
}
private void init(AttributeSet attrs){
setPaletteSize();
initAttrColor(attrs);
initPaint();
initPosition();
initRadiusWidth(attrs);
}
/**
* 用于设置中间圆的颜色
* @param color
*/
public void setCenterPaint(int color){
centerPaint.setColor(color);
}
/**
* 设置调色板的尺寸
*/
private void setPaletteSize(){
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);//获取WM对象
int height = (int) (manager.getDefaultDisplay().getHeight() * 0.5f);//获取屏幕的高度*0.5
int width = (int) (manager.getDefaultDisplay().getWidth() * 0.7f);//获取屏幕宽度的0.7
this.height = height - 36;
this.width = width;
setMinimumHeight(height - 36);
setMinimumWidth(width);
}
/**
* 设置颜色指示器的位置
* @param point
*/
public void setIndictorPosition(Point point){
if(point!=null) {
this.indictorPosition.x = point.x;
this.indictorPosition.y = point.y;
}
}
/**
* 设置是否显示颜色指示器
* @param isShow
*/
public static void setIndictorShow(boolean isShow){
RainbowPalette.isShowIndictor=isShow;
}
/**
* 设置指示器小球Color的默认值
* @param color
*/
public void setBallColor(int color){
this.indictorColor=color;
}
/**
* 初始化各种Paint对象
*/
private void initPaint(){
setGradientColors();
Shader shader = new SweepGradient(0, 0, gradientColors, null);//SweepGradient渐变
//外层彩虹光环
borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//在画图的时候,图片如果旋转或缩放之后,总是会出现那些华丽的锯齿。给Paint加上抗锯齿标志
borderPaint.setAntiAlias(true);
borderPaint.setShader(shader);//传入着色器
borderPaint.setStyle(Paint.Style.STROKE);//设置仅描边
borderPaint.setStrokeWidth(BORDER_WIDTH);//设置描边的宽度,直接对应
//初始化色环的Paint对象
palettePaint = new Paint(Paint.ANTI_ALIAS_FLAG);//在画图的时候,图片如果旋转或缩放之后,总是会出现那些华丽的锯齿。给Paint加上抗锯齿标志
palettePaint.setAntiAlias(true);
palettePaint.setShader(shader);//传入着色器
palettePaint.setStyle(Paint.Style.STROKE);//设置仅描边
palettePaint.setStrokeWidth(PALETTE_WIDTH);//设置描边的宽度,直接对应
//初始化中心圆的Paint对象
centerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
centerPaint.setAntiAlias(true);
centerPaint.setColor(centerCircleColor);
centerPaint.setStrokeWidth(CENTER_CIRCLE_WIDTH);
//初始化小球对象
indictorPosition=new Point();
indictorPaint= new Paint(Paint.ANTI_ALIAS_FLAG);
indictorPaint.setAntiAlias(true);
indictorPaint.setColor(indictorColor);
indictorPaint.setStrokeWidth(INDICTOR_WIDTH);
}
private void initAttrColor(AttributeSet attrs){
TypedArray types = context.obtainStyledAttributes(attrs,
R.styleable.rainbow_palette);
try {
centerCircleColor = types.getColor( R.styleable.rainbow_palette_center_circle_defcolor,DEF_CIRCLE_COLOR );
indictorColor = types.getColor( R.styleable.rainbow_palette_indicator_color,DEF_INDICTOR_COLOR );
} finally {
types.recycle(); // TypeArray用完需要recycle
}
}
/**
* 设置色环和中心圆、圆形指示小球的半径,宽度
*/
private void initRadiusWidth(AttributeSet attrs){
TypedArray types = context.obtainStyledAttributes(attrs,
R.styleable.rainbow_palette);
try {
paletteWidth = types.getDimensionPixelOffset( R.styleable.rainbow_palette_palette_width, PALETTE_WIDTH);
paletteRadius=types.getDimensionPixelOffset(R.styleable.rainbow_palette_palette_radius,(int)(wi dth / 2 - palettePaint.getStrokeWidth()*1.2f));
centerRadius=types.getDimensionPixelOffset(R.styleable.rainbow_palette_palette_radius,(int)((pa letteRadius - palettePaint.getStrokeWidth() / 2 ) * 0.5f));
indictorResId=types.getResourceId(R.styleable.rainbow_palette_ic_indicator,0);
if(indictorResId==0) {
//未指定指示器的图标采用默认的绘制一个小圆形
indictorRadius = (float) (centerRadius * 0.5);
}else {
initIndictorImg();//使用设置的指示器目标
}
} finally {
types.recycle(); // TypeArray用完需要recycle
}
}
/**
* 初始化颜色指示器,设置指定图片
*/
private void initIndictorImg(){
// 将背景图片大小设置为属性设置的直径
indicatorBitmap = BitmapFactory.decodeResource(getResources(), indictorResId );
//indicatorBitmap = Bitmap.createScaledBitmap(indicatorBitmap, (int)centerRadius,(int)centerRadius, false);
indicatorBitmap=
Bitmap.createScaledBitmap(indicatorBitmap,indicatorBitmap.getWidth(),indicatorBitmap.getHeig ht(),true);
indictorRadius=indicatorBitmap.getHeight();
}
/**
* 设置色环的绘制圆心
*/
private void initPosition(){
centerPosition=new Point();
centerPosition.set(width/2,height/2-50);
indictorPosition.set(0,0);
}
/**
* 设置渐变环的颜色取值
*/
public void setGradientColors(){
gradientColors = new int[] {0xFFFF0000, 0xFFFF00FF, 0xFF0000FF,0xFF00FFFF, 0xFF00FF00,0xFFFFFF00, 0xFFFF0000};
}
private void drawBorder(Canvas canvas){
borderPaint.setAlpha(220);
canvas.drawOval(new RectF(-(paletteRadius+55), -(paletteRadius+55), (paletteRadius+55), (paletteRadius+55)), borderPaint);//画次外圈
borderPaint.setAlpha(100);
canvas.drawOval(new RectF(-(paletteRadius+60), -(paletteRadius+60), (paletteRadius+60), (paletteRadius+60)), borderPaint);//画外圈
borderPaint.setAlpha(60);
canvas.drawOval(new RectF(-(paletteRadius+65), -(paletteRadius+65), (paletteRadius+65), (paletteRadius+65)), borderPaint);//画外圈
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(centerPosition.x,centerPosition.y);//移动中心,要不然会导致画出来之后不能完全显示,此时圆心相当于是由(0,0)变为(width / 2, height / 2 - 50)
canvas.drawCircle(0,0, centerRadius, centerPaint);//画中心圆
canvas.drawOval(new RectF(-paletteRadius, -paletteRadius, paletteRadius, paletteRadius), palettePaint);//画色环
drawBorder(canvas);
if(isShowIndictor) {
if (indictorResId == 0) {
canvas.drawCircle(indictorPosition.x, indictorPosition.y, (float) (centerRadius * 0.5), indictorPaint);//画颜色指示器小球
} else {
canvas.drawBitmap(indicatorBitmap, indictorPosition.x, indictorPosition.y, indictorPaint);//画指示器指定图片
}
}
Log.e("Position", "indictorPosition: X:"+indictorPosition.x+"Y:"+indictorPosition.y );
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(width, height);//重新设置View的位置,若不重写的话,则不会布局,即使设置centerInParent为true也无效
//setMeasuredDimension(width,height);
}
// 可以实现点击的时候显示对应的指示点,但会超过边界
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX() - width / 2;//event.getX()以父视图的左上角作为原点
float y = event.getY() - height / 2 + 50;
boolean isEcfect = isEfectiveTouch(x, y,
paletteRadius + palettePaint.getStrokeWidth() / 2, paletteRadius - palettePaint.getStrokeWidth() / 2);
int choosedColor;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if(isEcfect){
float angle = (float) Math.atan2(y, x);
float unit = (float) (angle / (2 * Math.PI));
if (unit < 0) {
unit += 1;
}
choosedColor=getColorByTouchPalette(gradientColors,unit);
indictorPosition.set((int) (event.getX()-(width / 2)), (int) (event.getY()-(width / 2)));
centerPaint.setColor(choosedColor);
if(listen!=null) {
listen.onColorChange(choosedColor);
}
}
else {
return false;
}
break;
case MotionEvent.ACTION_MOVE:
if(isEcfect){
float angle = (float) Math.atan2(y, x);
float unit = (float) (angle / (2 * Math.PI));
if (unit < 0) {
unit += 1;
}
choosedColor=getColorByTouchPalette(gradientColors,unit);
indictorPosition.set((int) (event.getX()-(width / 2)), (int) (event.getY()-(width / 2)));
centerPaint.setColor(choosedColor);
invalidate();
}
break;
case MotionEvent.ACTION_UP:
if(isEcfect){
float angle = (float) Math.atan2(y, x);
float unit = (float) (angle / (2 * Math.PI));
if (unit < 0) {
unit += 1;
}
choosedColor=getColorByTouchPalette(gradientColors,unit);
indictorPosition.set((int) (event.getX()-(width / 2)), (int) (event.getY()-(width / 2)));
centerPaint.setColor(choosedColor);
if(listen!=null) {
listen.onColorChange(choosedColor);
listen.onColorChangeUp((int) (event.getX()-(width / 2)), (int) (event.getY()-(width / 2)));
}
}
return true;
default:
break;
}
if (isEcfect) {
invalidate();
}
return true;
}
/**
* 获取圆环上颜色
* @param colors
* @param unit
* @return
*/
private int getColorByTouchPalette(int colors[], float unit) { if (unit <= 0) {
return colors[0];
}
if (unit >= 1) {
return colors[colors.length - 1];
}
float p = unit * (colors.length - 1);
int i = (int)p;
p -= i;
// now p is just the fractional part [0...1) and i is the index int c0 = colors[i];
int c1 = colors[i+1];
int a = ave(Color.alpha(c0), Color.alpha(c1), p);
int r = ave(Color.red(c0), Color.red(c1), p);
int g = ave(Color.green(c0), Color.green(c1), p);
int b = ave(Color.blue(c0), Color.blue(c1), p);
return Color.argb(a, r, g, b);
}
private int ave(int s, int d, float p) {
return s + Math.round(p * (d - s));
}
/**
* 是否是有效Touch即是否是按在圆环之上的
* @return
*/
private boolean isEfectiveTouch(float x, float y, float outRadius, float inRadius){ double outCircle = Math.PI * outRadius * outRadius;
double inCircle = Math.PI * inRadius * inRadius;
double fingerCircle = Math.PI * (x * x + y * y);
if(fingerCircle < outCircle && fingerCircle > inCircle) {
return true;
}else {
return false;
}
}
/**
* @describe 勾股定理求触摸点与圆心之间直线与水平方向的夹角角度
* @param a 触摸点
* @param b 圆心
* @return
*/
public float getRadian(Point a, Point b) {
float lenA = Math.abs(b.x - a.x);
float lenB = Math.abs(b.y - a.y);
float lenC = (float) Math.sqrt(lenA * lenA + lenB * lenB);
float ang = (float) Math.acos(lenA / lenC);
ang = ang * (b.y < a.y ? -1 : 1);
return ang;
}
/**
* 设置小球的绘制位置,只能绘制在色环内
* @return
*/
private Point setIndictorPositionBorder(int x, int y){
Point touchPosition=new Point(x,y);
Point centerCircle=new Point(0,0);
float radian=getRadian(touchPosition,centerCircle);
float distance=getTwoPointDistance(centerCircle,touchPosition);//touch点和圆心之间的距离
float touchToBallDistance;//touch点和内切时小球圆心所在的位置之间的距离,如果不超出色环之外,这两点位置重合,如果超出了则自动移到内切位置处
if(distance+indictorRadius>(int)paletteRadius){
touchToBallDistance= Math.abs(distance-paletteRadius+indictorRadius);//touch点和内切(与外环)时小球圆心所在的位置之间的距离
if(Math.abs(Math.cos(radian))==1){//如果夹角为0或者180°
if(x<0){
indictorPosition.set(-(int)(paletteRadius-20),0);
}else {
indictorPosition.set((int)(paletteRadius-20),0);
}
}
indictorPosition.set((int)(x-(Math.cos(radian)*touchToBallDistance)),(int)(y-(Math.sin(radian)*to uchToBallDistance)));
}else if(distance<((int)paletteRadius-160)){
touchToBallDistance=
Math.abs(paletteRadius-160-distance+indictorRadius);//touch点和外切(与内环)时小球圆心所在的位置之间的距离
indictorPosition.set((int)(x+(Math.cos(radian)*touchToBallDistance)),(int)(y+(Math.sin(radian)*t ouchToBallDistance)));
}else {
indictorPosition.set(x,y);
}
return indictorPosition;
}
/**
* 求两点之间的距离
* @param a
* @param b
* @return
*/
public float getTwoPointDistance(Point a, Point b){
return (float) Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2));
}
public void setOnChangeListen(OnColorChangedListen listen){
this.listen=listen;
}
public interface OnColorChangedListen{
void onColorChange(int color);//自定义的颜色切换的时候触发的回调
void onColorChangeUp(int x,int y);
}
}
四、应用彩虹圆环调色板
MainActivity.Java
public class MainActivity extends AppCompatActivity implements RainbowPalette.OnColorChangedListen ,View.OnClickListener,SeekBar.OnSeekBarChangeListen er{
private RainbowPalette rainbowPalette;
private TextView txtTitle,txtChoosePre,txtChooseNext;
private SeekBar seekBar;
private SparseArray sparseArray;
private int currentIndex = 6;//当前的颜色的index,默认为蓝色
private int currentColor;//选中的颜色
private String currentRGB;
private int[] ledNormalColor = {0xFFFFFFFF, 0xFFFF0000, 0xFFF3990C, 0xFFEEF60B, 0xFF3C981B, 0xFF3CE2F3, 0xFF0511FB, 0xFFAB56EE};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(https://www.sodocs.net/doc/3f18806220.html,yout.activity_main);
init();
}
private void initViews(){
rainbowPalette= (RainbowPalette) findViewById(R.id.imv_led_palettle);
txtTitle= (TextView) findViewById(R.id.id_txt_palette);
txtChoosePre= (TextView) findViewById(https://www.sodocs.net/doc/3f18806220.html,_choose_pre);
txtChooseNext= (TextView) findViewById(https://www.sodocs.net/doc/3f18806220.html,_choose_next);
seekBar= (SeekBar) findViewById(R.id.id_alpha_seek_bar);
txtChoosePre.setOnClickListener(this);
txtChooseNext.setOnClickListener(this);
rainbowPalette.setOnChangeListen(this);
seekBar.setOnSeekBarChangeListener(this);
}
private void init(){
initViews();
initColorPosition();
}
/**
* 手动设置指示小球显示的位置
*/
public void setIndictorPosition(int index){
if (sparseArray!=null) {
rainbowPalette.setIndictorPosition((Point)
sparseArray.get(ledNormalColor[index]));
rainbowPalette.setCenterPaint((ledNormalColor[currentIndex]));
}
RainbowPalette.isNeedShowIndictor=false;
rainbowPalette.invalidate();
}
private void onChooseColor(View view){
switch (view.getId()){
case https://www.sodocs.net/doc/3f18806220.html,_choose_pre:
setIndictorPosition(choosePreColor());
break;
case https://www.sodocs.net/doc/3f18806220.html,_choose_next:
setIndictorPosition(chooseNextColor());
break;
default:
break;
}
}
/**
* 选择前一种颜色,并返回对应的索引
*
* @return
*/
private int choosePreColor() {
int size = ledNormalColor.length;
if (currentIndex == 0) {
currentIndex = size;
}
currentIndex = currentIndex - 1;
currentColor = ledNormalColor[currentIndex];
return currentIndex;
}
/**
* 选择后一种颜色,并返回对应的索引
*
* @return
*/
private int chooseNextColor() {
int size = ledNormalColor.length;
if (currentIndex == size - 1) {
currentIndex = -1;
}
currentIndex = currentIndex + 1;
currentColor = ledNormalColor[currentIndex];
return currentIndex;
}
/**
* 初始化固定颜色对应的坐标值
*/
private void initColorPosition(){
Point[] points={new Point(-75,1),new Point(110,-2),new Point(102,-88),new Point(57,-120),
new Point(-69,-131),new Point(-145,-18),new Point(-64,102),new Point(44,103)} ;
sparseArray = new SparseArray
for(int i=0;i sparseArray.append(ledNormalColor[i],points[i] ); } } /** * 获取argb模式的颜色值并转为字符串 * @param color * @return */ private String parseArgb(int color){ int a = (color >>> 24); int r = (color >> 16) & 0xFF; int g = (color >> 8) & 0xFF; int b = (color) & 0xFF; return String.valueOf(a)+String.valueOf(r)+String.valueOf(g)+String.valueOf(b); } private String parseRGB(int color){ int r = (color >> 16) & 0xFF; int g = (color >> 8) & 0xFF; int b = (color) & 0xFF; return String.valueOf(r)+String.valueOf(g)+String.valueOf(b); } @Override public void onColorChange(int color) { txtTitle.setTextColor(color); currentColor=color; currentRGB=Integer.toHexString(color); Log.e("Color", "onColorChange: "+ Color.red(color)+Color.green(color)+Color.blue(color)); Log.e("Color", "onColorChange:parseArgb "+parseArgb(color) ); Log.e("Color", "onColorChange:parseRGB "+parseRGB(color) ); Log.e("Color", "onColorChange: "+Integer.toHexString(color) );//获取十进制字符串表示argb模式的颜色0xFFF3990C-->fff3990c } /** * 获取最终的颜色值ARGB模式的 * @param progress * @return */ private int getChangedColor(int progress){ String red,green,blue; if(https://www.sodocs.net/doc/3f18806220.html,ress==0){ progress=1; } if(currentRGB==null){ currentRGB="FF0511FB"; } red=currentRGB.substring(2,4); green=currentRGB.substring(4,6); blue=currentRGB.substring(6); return Color.argb(progress,Integer.parseInt(red,16),Integer.parseInt(green,16),Integer.parseInt(blue,16)); } @Override public void onClick(View v) { onChooseColor(v); } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { currentColor = getChangedColor(progress); rainbowPalette.setCenterPaint(currentColor); Log.e("Color", "onProgressChanged: "+Integer.toHexString(currentColor) ); rainbowPalette.invalidate(); txtTitle.setTextColor(currentColor); Log.e("Color", "onProgressChanged: rgb"+ Color.red(currentColor)+Color.green(currentColor)+Color.blue(currentColor)); } @Override public void onStartTrackingTouch(SeekBar seekBar) { return; } @Override public void onStopTrackingTouch(SeekBar seekBar) { return; } } activity_main.xml android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorBcg"> android:id="@+id/id_txt_palette" android:textSize="22sp" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="自定义的调色板CrazyMo"/> android:id="@+id/rl_led_paletle" android:layout_below="@id/id_txt_palette" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="86dp" android:gravity="https://www.sodocs.net/doc/3f18806220.html,nter" android:layout_alignParentTop="true" > android:id="@+id/tv_choose_pre" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_centerInParent="true" android:layout_marginLeft="24dp" android:layout_marginRight="24dp" android:background="@mipmap/bcg_pre_btn" android:clickable="https://www.sodocs.net/doc/3f18806220.html,ue"/> android:id="@+id/tv_choose_next" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_centerInParent="true" android:layout_marginRight="24dp" android:layout_marginLeft="24dp" android:background="@mipmap/bcg_next_btn" android:clickable="true"/> crazymo:ic_indicator="@mipmap/src_indicator" crazymo:center_circle_defcolor="#0511FB" android:id="@+id/imv_led_palettle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:src="@mipmap/img_led_palette" /> android:id="@+id/rl_led_switch" android:layout_width="match_parent" android:layout_height="58dp" android:layout_marginLeft="24dp" android:layout_marginRight="24dp" android:orientation="horizontal" android:layout_marginTop="30dp" android:background="@drawable/shape_list_border" android:layout_below="@+id/rl_led_paletle" android:layout_centerHorizontal="true"> android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:paddingLeft="12dp" android:text=" 彩灯开关" /> android:id="@+id/cb_contrl_led" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="12dp" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:button="@null" android:checked="true" android:background="@drawable/selector_color"/> android:layout_below="@id/rl_led_switch" android:layout_width="match_parent" android:layout_height="58dp" android:layout_marginLeft="24dp" android:layout_marginRight="24dp" android:orientation="horizontal" android:background="@drawable/shape_list_border" android:layout_centerHorizontal="true" android:gravity="center_horizontal" > android:id="@+id/tv_set_alpha" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:paddingLeft="12dp" android:text=" 亮度调节" /> android:id="@+id/id_alpha_seek_bar" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:layout_alignRight="@id/tv_set_alpha" android:layout_gravity="center_vertical" android:progress="255" android:max="255" /> android 自定义圆角头像以及使用declare-styleable进行配置属性解析由于最新项目中正在检查UI是否与效果图匹配,结果关于联系人模块给的默认图片是四角稍带弧度的圆角,而我们截取的图片是正方形的,现在要给应用统一替换。应用中既用到大圆角头像(即整个头像是圆的)又用到四角稍带弧度的圆角头像,封装一下以便重用。以下直接见代码 [java] view plain copy 在CODE上查看代码片派生到我的代码片 package com.test.demo; import com.test.demo.R; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.RectF; import android.graphics.Shader.TileMode; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.widget.ImageView; /** * 圆角imageview */ public class RoundImageView extends ImageView { private static final String TAG = "RoundImageView"; /** * 图片的类型,圆形or圆角 */ private int type; public static final int TYPE_CIRCLE = 0; public static final int TYPE_ROUND = 1; /** * 圆角大小的默认值 android studio 控件常用属性 下面是RelativeLayout各个属性 1.android:layout_above="@id/xxx" --将控件置于给定ID控件之上 2.android:layout_below="@id/xxx" --将控件置于给定ID控件之下 3. android:layout_toLeftOf="@id/xxx" --将控件的右边缘和给定ID控件的左边缘对齐 4.android:layout_toRightOf="@id/xxx" --将控件的左边缘和给定ID控件的右边缘对齐 5. android:layout_alignLeft="@id/xxx" --将控件的左边缘和给定ID控件的左边缘对齐 6.android:layout_alignTop="@id/xxx" --将控件的上边缘和给定ID控件的上边缘对齐 7.android:layout_alignRight="@id/xxx" --将控件的右边缘和给定ID控件的右边缘对齐 8.android:layout_alignBottom="@id/xxx" --将控件的底边缘和给定ID控件的底边缘对齐 9.android:layout_alignParentLeft="true" --将控件的左边缘和父控件的左边缘对齐 10. android:layout_alignParentTop="true" --将控件的上边缘和父控件的上边缘对齐 11. android:layout_alignParentRight="true" --将控件的右边缘和父控件的右边缘对齐 12.android:layout_alignParentBottom="true" --将控件的底边缘和父控件的底边缘对齐 13.android:layout_centerInParent="true" --将控件置于父控件的中心位置 14.android:layout_centerHorizontal="true" --将控件置于水平方向的中心位置 15.android:layout_centerVertical="true" --将控件置于垂直方向的中心位置 android:layout_width 设置组件的宽度 android:layout_height 设置组件的高度 android:id 给组件定义一个id值,供后期使用 android:background 设置组件的背景颜色或背景图片 android:text 设置组件的显示文字 android:textColor 设置组件的显示文字的颜色 android:layout_below 组件在参考组件的下面 android:alignTop 同指定组件的顶平行 Android平台我的日记 设计文档 项目名称:mydiray 项目结构示意: 阶段任务名称(一)布局的设计 开始时间: 结束时间: 设计者: 梁凌旭 一、本次任务完成的功能 1、各控件的显示 二、最终功能及效果 三、涉及知识点介绍 四、代码设计 activity_main.xml: android:layout_centerHorizontal="true" android:layout_marginTop="88dp" android:text="@string/wo" android:textSize="35sp"/> 《Android基础应用》 Android常用控件 ?本章任务 ?使用Android开发使用时间组件 ?使用Android开发使用进度条组件 ?使用Android开发创建底部选项卡 ?本章目标 ?了解Android的组件层次结构 ?掌握常用的日期时间类控件 ?掌握常用的几个容器组件 1.Android组件的层次结构 UI组件都是View的子类,View有很多子类,它们之间存在树状的继承关系View及其子类结构图 TextView及其子类结构图 ViewGroup及其子类结构图 其下的子类一般作为容器或布局来使用 FrameLayout及其子类结构图 其下的子类通常作为容器或布局来使用 2.时间控件 2.1日期时间选择器 DatePicker组件可用于输入日期,TimePicker组件可用来选择时间,只能输入小时和分,默认12小时制 DatePicker ●使用onDateChangedListener监听器来获取用户的日期选择 ●使用init对组件进行初始化 ●使用getYear,getMonth,getDayOfMonth方法获得用户选择的年,月,日 TimePicker ●使用onTimeChangedListener监听器获取用户的时间选择 ●使用setIs24HourView设置是否以24小时制显示 ●使用getCurrentHour获得当前的小时数 ●使用getCurrentMinute获得当前的分钟数 示例 示例的实现结果 2.2时钟组件 AnalogClock组件用来以表盘的方式显示当前时间,该表只有时针和分针,DigitClock组件以数字的方式显示当前时间可以显示时分秒,由于DigitClock继承TextView,可以使用TextView 的属性 示例 android 自定义控件的过程 invalidate()会导致computeScroll()以及onDraw()方法的执行computeScroll()方法是在屏幕流动的时候不停的去调用,scrollTo(int x,int y)则是滚动到相应的位置; scrollBy(int x, int y)则是移动一些距离,X为正是向左移动,为负时向右移动,Y与X的意义一个,只是是上下移动而已View对象显示在屏幕上,有几个重要步骤: 1.构造方法创建对象 2.测量View的大小onMeasure(int,int); 3.确定View的位置,View自身有一些权,决定权在父View手中. onLayout();基本上不常用,在继承View的时候基本上用不着,但在继承ViewGroup的时候的就要用到了,因为要对View进行布局,确定View的位置,确定的时候使用 指定子View的位置,左,上,右,下,是指在ViewGroup坐标系中的位置https://www.sodocs.net/doc/3f18806220.html,yout(int xtop,int ytop, int xbottom, int ybottom); 4.绘制View的内容onDraw(Canvas) 实现过程: 1、构造方法: /** * 在布局文件中声名的view,创建的时候由系统调用 * * @param context * 上下文对象 * @param attrs * 属性集 */ public MyToggleButton(Context context, AttributeSet attrs) { super(context, attrs); initView(); } 2、测量View的大小: /** * 测量尺寸时的回调方法 */ android常用控件大全 在Android中使用各种控件(View) DatePicker-日期选择控件 TimePicker-时间选择控件 ToggleButton-双状态按钮控件 EditText-可编辑文本控件 ProgressBar-进度条控件 SeekBar-可拖动的进度条控件 AutoCompleteTextView-支持自动完成功能的可编辑文本控件 MultiAutoCompleteTextView-支持自动完成功能的可编辑文本控件,允许输入多值(多值之间会自动地用指定的分隔符分开) ZoomControls-放大/缩小按钮控件 Include-整合控件 VideoView-视频播放控件 WebView-浏览器控件 RatingBar-评分控件 Tab-选项卡控件 Spinner-下拉框控件 Chronometer-计时器控件 ScrollView-滚动条控件 在Android中使用的Layout FrameLayout:里面只可以有一个控件,并且不能设计这个控件的位置,控件会放到左上角 LinearLayout:里面可以放多个控件,但是一行只能放一个控件 TableLayout:这个要和TableRow配合使用,很像html里面的table AbsoluteLayout:里面可以放多个控件,并且可以自己定义控件的x,y的位置 RelativeLayout:里面可以放多个控件,不过控件的位置都是相对位置 (Android界面布局好像还可以直接引用一些view,如ScrollView等) 常用控件: 1,EditText 主要函数:setText/getText设置/获取文本内容,setHint设置缺省显示内容; 2,RadioGroup,RadioButton RadioButton的isChecked()判断是否被选中 获取选中RadioButon的ID:设置 RadioGroup.setOnCheckedChangeListener方法 publiconCheckedChanged(RadioGroupgroup,intcheckedId)//checkedId 是选中RadioButton的ID 3,CheckBox isChecked()判断是否被选中 setOnCheckedChangeListener方法监视选中状态改变情况 4,Spinner a,显示数据 1),硬编码加载 通过setAdapter方法设置类型为 ArrayAdapter(Contextcontext,inttextViewResId,String[]objects) textViewResourceId:显示内容的ViewID默认设置为 https://www.sodocs.net/doc/3f18806220.html,yout.simple_spinner_item Android进阶——自定义View之自己绘 制彩虹圆环调色板 引言 前面几篇文章都是关于通过继承系统View和组合现有View来实现自定义View的,刚好由于项目需要实现一个滑动切换LED彩灯颜色的功能,所以需要一个类似调色板的功能,随着手在调色板有效区域滑动,LED彩灯随即显示相应的颜色,也可以通过左右的按钮,按顺序切换显示一组颜色,同时都随着亮度的改变LED彩灯的亮度随即变化,这篇基本上把继承View重绘实现自定义控件的大部分知识总结了下(当然还有蛮多没有涉及到,比如说自适应布局等),源码在Github上 一、继承View绘制自定义控件的通用步骤 自定义属性和继承View重写onDraw方法 实现构造方法,其中public RainbowPalette(Context context, AttributeSet attrs) 必须实现,否则无法通过xml引用,public RainbowPalette(Context context) ,public RainbowPalette(Context context, AttributeSet attrs, int defStyleAttr)可选,通常在构造方法中完成属性和其他成员变量的初始化 重写onMeasure方法,否则在xml中有些设置布局参数无效 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(width, height);//重新设置View的位置,若不重写的话,则不会布局,即使设置centerInParent为true也无效 //setMeasuredDimension(width,height); } 手动调用invalidate或者postInvalidateon方法完成界面刷新 重写onTouchEvent方法实现基本的交互 定义回调接口供外部调用 二、彩虹圆环调色板设计思想 自定义Dialog; dialog = new Dialog(this); dialog.setContentView(https://www.sodocs.net/doc/3f18806220.html,yout.by_baseinfo); dialog.setTitle("dialog的title"); /* * 获取Dialog的窗口对象及参数对象以修改对话框的布局设置, 可以直接调用this.getWindow(),表示获得这个Activity的Window * 对象,这样这可以以同样的方式改变这个Activity的属性. * Activity不可见时getWindow()返回值为null; */ Window dialogWindow = dialog.getWindow(); // 对话框的布局设置参数; https://www.sodocs.net/doc/3f18806220.html,youtParams layoutParams = dialogWindow.getAttributes(); // 设置Window中的内容为左上对齐; dialogWindow.setGravity(Gravity.LEFT | Gravity.TOP); /* * lp.x与lp.y表示相对于原始位置的偏移. * 当参数值包含Gravity.LEFT时,对话框出现在左边,所以lp.x就表示相对左边的偏移,负值忽略. * 当参数值包含Gravity.RIGHT时,对话框出现在右边,所以lp.x就表示相对右边的偏移,负值忽略. * 当参数值包含Gravity.TOP时,对话框出现在上边,所以lp.y就表示相对上边的偏移,负值忽略. * 当参数值包含Gravity.BOTTOM时,对话框出现在下边,所以lp.y就表示相对下边的偏移,负值忽略. * 当参数值包含Gravity.CENTER_HORIZONTAL时 * ,对话框水平居中,所以lp.x就表示在水平居中的位置移动lp.x像素,正值向右移动,负值向左移动. * 当参数值包含Gravity.CENTER_VERTICAL时 * ,对话框垂直居中,所以lp.y就表示在垂直居中的位置移动lp.y像素,正值向右移动,负值向左移动. * gravity的默认值为Gravity.CENTER,即Gravity.CENTER_HORIZONTAL | * Gravity.CENTER_VERTICAL. * * 本来setGravity的参数值为Gravity.LEFT | Gravity.TOP时对话框应出现在程序的左上角,但在 * 我手机上测试时发现距左边与上边都有一小段距离,而且垂直坐标把程序标题栏也计算在内了, Gravity.LEFT, Gravity.TOP, * Gravity.BOTTOM与Gravity.RIGHT都是如此,据边界有一小段距离 */ // 相对于屏幕原位置(加上标题栏) 的偏移量; lp.x = 100; // 新位置X坐标 今天和大家分享下组合控件的使用。很多时候android自定义控件并不能满足需求,如何做呢?很多方法,可以自己绘制一个,可以通过继承基础控件来重写某些环节,当然也可以将控件组合成一个新控件,这也是最方便的一个方法。今天就来介绍下如何使用组合控件,将通过两个实例来介绍。 第一个实现一个带图片和文字的按钮,如图所示: 整个过程可以分四步走。第一步,定义一个layout,实现按钮内部的布局。代码如下: 1. 2. 实验二 android基本控件实验 【目的】 熟悉Android常用控件的基本操作,掌握它们的基本使用方法。了解控件之间的构成关系,熟悉适配器的使用原理。 【要求】 1、了解控件的继承关系; 2、掌握适配器的使用; 3、掌握信息提示的实现机制; 4、实现基本界面。 【原理】 1.控件类之间的关系 android.view.View类(视图类)呈现了最基本的UI构造块。View类是Android中的一个超类,几乎包含了所有的屏幕类型,主要负责绘制和事件处理。 Android中控件类的扩展结构如图所示。 View有众多的扩展者,它们大部分是在android.widget包中,这些继承者实际上就是Android 系统中的“控件”。View实际上就是各个控件的基类,创建交互式的图形用户界面的基础。View的直接继承者包括文本视图(TextView)、图像视图(ImageView)、进度条(ProgressBar)等。它们各自又有众多的继承者。每个控件除了继承父类功能之外,一般还具有自己的公有 方法、保护方法、XML属性等。 在Android中使用各种控件的一般情况是在布局文件中可以实现UI的外观,然后在Java文件中实现对各种控件的控制动作。控件类的名称也是它们在布局文件XML中使用的标签名称。 2.控件通用行为和属性 View是Android中所有控件类的基类,因此View中一些内容是所有控件类都具有的通用行为和属性。 提示:由于Java语言不支持多重继承,因此Android控件不可能以基本功能的“排列组合”的方式实现。在这种情况下,为了实现功能的复用,基类的功能往往做得较强,作为控件的祖先类,View所实现的功能也是最多的。 控件类经常在布局文件中使用,因此其可以使用XML属性(XMLAttributes),和Java代码经常具有对应关系。 View作为各种控件的基类,其XML属性所有控件通用,XML属性及其对应的方法如表1所示。 表1 View中的XML属性及其对应的方法 其中,android:id表示控件的标识,通常需要在布局文件中指定这个属性。View中与控件标识相关的几个方法如下所示: public int getId() // 获得控件的id(int类型) android自定义View之Android手机通讯录制作 我们的手机通讯录一般都有这样的效果,如下图: OK,这种效果大家都见得多了,基本上所有的Android手机通讯录都有这样的效果。那我们今天就来看看这个效果该怎么实现。 一.概述 1.页面功能分析 整体上来说,左边是一个ListView,右边是一个自定义View,但是左边的ListView 和我们平常使用的ListView还有一点点不同,就是在ListView中我对所有的联系人进行了分组,那么这种效果的实现最常见的就是两种思路: 1.使用ExpandableListView来实现这种分组效果 2.使用普通ListView,在构造Adapter时实现SectionIndexer接口,然后在Adapter 中做相应的处理 这两种方式都不难,都属于普通控件的使用,那么这里我们使用第二种方式来实现,第一种方式的实现方法大家可以自行研究,如果你还不熟悉ExpandableListView的使用,可以参考我的另外两篇博客: 1.使用ExpandableListView实现一个时光轴 2.android开发之ExpandableListView的使用,实现类似QQ好友列表 OK,这是我们左边ListView的实现思路,右边这个东东就是我们今天的主角,这里我通过自定义一个View来实现,View中的A、B......#这些字符我都通过canvas的drawText 方法绘制上去。然后重写onTouchEvent方法来实现事件监听。 2.要实现的效果 要实现的效果如上图所示,但是大家看图片有些地方可能还不太清楚,所以这里我再强调一下: 1.左边的ListView对数据进行分组显示 2.当左边ListView滑动的时候,右边滑动控件中的文字颜色能够跟随左边ListView 的滑动自动变化 3.当手指在右边的滑动控件上滑动时,手指滑动到的地方的文字颜色应当发生变化,同时在整个页面的正中央有一个TextView显示手指目前按下的文字 Android高手进阶教程(三)之----Android 中自定义View的应用. 2010-04-18 21:11:25 标签:Android进阶View定义教程 原创作品,允许转载,转载时请务必以超链接形式标明文章原始出处、作者信息和本声明。否则将追究法律责任。https://www.sodocs.net/doc/3f18806220.html,/1556324/311457 大家好我们今天的教程是在Android 教程中自定义View 的学习,对于初学着来说,他们习 惯了Android 传统的页面布局方式,如下代码: view plaincopy to clipboardprint? 1. 2. 创建控件并添加监听器的过程: 1.xml中定义控件() 2.Activity中实例化控件((Button)findViewById()) 3.创建监听器(OnClickListener) 4.绑定监听器(setOnClickListener) UI基础-内/外边距 UI基础-常用控件-单选按钮 UI基础-常用控件-输入控件 maxLines:最大行数。 maxLength:最大字符数。 hint:空白文字提示,当输入文字时自动消失。 password:密码文本框。 numeric:控制输入的数字类型,一共有三种分别为integer(正整数)、signed(带符号整数,有正负)和decimal(浮点数)。 digits:录入指定字符。 UI基础-常用控件-图片控件 CENTER:图片大小为原始大小,如果图片大小大于ImageView控件,则截取图片中间部分,若小于,则直接将图片居中显示。 CENTER_CROP:将图片等比例缩放,让图像的短边与ImageView的边长度相同,即不能留有空白,缩放后截取中间部分进行显示。 CENTER_INSIDE:将图片大小大于ImageView的图片进行等比例缩小,直到整幅图能够居中显示在ImageView中,小于ImageView的图片不变,直接居中显示。 FIT_CENTER:ImageView的默认状态,大图等比例缩小,使整幅图能够居中显示在ImageView中,小图等比例放大,同样要整体居中显示在ImageView中。 FIT_START:缩放方式同FIT_CENTER,只是将图片显示在左方或上方,而不是居中。FIT_END:缩放方式同FIT_CENTER,只是将图片显示在左方或上方,而不是居中。 FIT_XY:将图片非等比例缩放到大小与ImageView相同。 基于Android的自定义媒体播放控件设计与实现 摘要:针对日益增加的个性化应用需求,提出了基于Android的自定义媒体播放控件。该控件通过继承VideoView 实现视频、图片媒体的播放,与网络、数据库进行结合可以下载媒体资源与管理资源。控件中定义了下载回调,即实时显示当前的、下载进度,因而有助于提升用户体验。 关键词:Android;自定义控件;媒体播放;VideoView DOIDOI:10.11907/rjdk.161461 中图分类号:TP319 文献标识码:A 文章编号:1672-7800(2016)005-0079-03 0 引言 在Android系统中,提供了很多控件用于Android应用的开发,其控件的丰富性能可满足基本的应用开发需求。但是随着定制应用的日益增加,基本的控件已很难满足需求,从另一方面而言,这也约束了个性化应用的发展。Google提供的自定义控件方法可以达到应用开发的个性化要求[1]。在Android程序中,视频媒体播放使用VideoView控件实现,或者使用MediaPlayer与SurfaceView结合实现媒体播放功能。对于图片的显示则使用ImageView来实现[2]。日常生活中,视频和图片都是大众最常见的媒体,在一个界面上要既能显 示图片又能显示视频,所以,本文提出了一种继承VideoView 的自定义媒体播放控件,该控件不仅可以显示图片,还可以播放视频,并且在下载视频的过程中可以显示下载进度。同时,自定义媒体播放控件和数据库结合,能够实现媒体文件的自动循环播放。 1 Android系统 Android系统由Google公司2007年在Google I/O开发者大会上发布的移动操作系统,Google将其源码开放以供广大开发者研究。Android系统采用分层架构,具体分为Applications、Application Framework、Libraries(包含Android Runtime)、Linux Kernel四层。Android应用开发者最常接触的是前两层,后两层主要用于底层库和硬件驱动等[3-5]。 2 View及其自定义媒体控件相关类 2.1 View类介绍 在Android系统的Application Framework层,提供了丰富的UI控件,所有UI控件都是直接或间接继承View类。View 类是所有UI控件的基类,该类表示了用户界面的基本构建模块――一个View占用屏幕的矩形区域并且负责界面绘制和事件处理[6-7]。 View类中有很多方法,这些方法都与其界面绘制和事件处理相关,下面简单介绍几个方法: ①onMeasure(int,int):该方法用于获取控件的宽、高, 任务一Android 开发环境的搭建 第一部分知识回顾与思考 1. Android 的四层架构分别包括哪几层?分别起到什么作用? 答: Linux 内核层( Linux Kernel ):基于Linux 内核,内核为上层系统提供了系统服务。 系统库层( Libraries ):系统库基于C/C++ 语言实现,通过接口向应用程序框架层提供编程接口。 应用框架层( Application Framework ):为开发者提供了一系列的Java API,包括图形用户界面组件View 、 SQLite 数据库相关的API 、 Service 组件等。 应用程序层( Applications ):包含了Android 平台中各式各样的应用程序。 第二部分职业能力训练 一、单项选择题(下列答案中有一项是正确的,将正确答案填入括号内) 1. Android 四层架构中,应用框架层使用的是什么语法?(C) A . C B . C++C. Java D. Android 2. Android 四层架构中,系统库层使用的是什么语法?(B) A . V B B. C /C++C. Java D .Android 3.应用程序员编写的Android 应用程序,主要是调用(B)提供的接口进行实现。 A .应用程序层B.应用框架层C.应用视图层D.系统库层 二、填空题(请在括号内填空) 1.在 Android 智能终端中,有很多应用如拍照软件、联系人管理软件,它们都属于Android 的(应用程序)层。 2.为了让程序员更加方便的运行调试程序,Android 提供了(模拟器),可以方便的将程序运行其上, 而不要实际的移动终端。 3.为了支持Java 程序运行,我们需要安装(JDK )。 三、简答题 1.简述 Android 开发环境安装的步骤。 答:下载并安装JDK ,配置 JDK 的环境变量; 从 Anroid 官网上下载Android 开发组件(包含Eclipse 和 Android SDK 、 ADT ); 安装 Android 开发环境(包括配置Android SDK 的环境变量、打开 Eclipse 通过菜单设定Android SDK 路径)。 2.简述 Android 应用程序创建和运行的步骤。 自定义Android带图片的按钮 前言 现在移动设备的按钮设计讲究大图标小文字,希望用户只要一看到图标便能知道这个按钮是干嘛的,但又要有必要的文字提示,最常见的就数搜索按钮了,上面一个大大的放大镜图标,下面两个字——搜索。 Bill最近也在做具有这种效果的按钮,过程总是曲折的,不过结果总是美好滴~现在Bill把其做法分享给大家,希望对还不会的朋友有所帮助。 先看看bill曲折的过程吧,也许里面就有你的影子: 最开始以为直接利用Android控件ImageButton即可完事,谁知事不如人料,ImageButton 只能显示图片,并不能对其添加文字,此想法不攻自破。 于是我想到了直接用Button,但是Button的文字却是显示在图片内部,并不能达到我的需求。放弃。 懒人总有懒人的办法,我可以直接在图片下方PS需要的文字嘛,然后把P好的图片放进ImageButton就好了。此法十分简单好用。但是,一旦我们需要改变文字,或者我要把文字显示在图片顶部而不是底部怎么办?此法虽简单,却缺乏可变性。放弃。 这就是所谓的“一钮三折”了~ 那么,有没有一种方法既能够拥有Button的效果,又能够实现Button显示的自定义呢? 答案是肯定的,接下来,bill将一步一步详细解释这个按钮的制作过程。 思路 首先,我们来看一下这个按钮的实现思路。有一种思维方式叫做“out of box”,也就是鼓励大家跳出固定思维模式以寻求新的突破。但是在“跳出箱子”之前,我们必须首先知道困住我们思维的“箱子”是什么。 在这里,这个箱子就是“按钮”。我们一直在想,如何去实现这个“按钮”,怎么才能让“按钮”显示出图片,然后在图片下面还显示一行字。我们就在“按钮”这个箱子里纠结。 但实际上,当我们发现所谓的“按钮”其实就是一个View的时候,一切就变得简单了。 它只不过是一个可点击、可设置监听、可显示文字或者图片的View而已。那么我们就跳出Android给我们设置的这个箱子,自己重新造一个具有我们需要的功能和外观的View 不就OK了? 经过分析,上述按钮效果实际上就是一个布局,一个最简单不过的垂直线性布局,上部分是一个ImageView,下部分是一个TextView,这个布局可点击、可设置监听。 我们首先要编写自己的ImageButton类,然后在主布局文件中为我们自定义的Button 编写布局,最后在Activity中调用我们自定义ImageButton即可。 那么接下来我们就一起来实现这个简单的LinearLayout。 编码实现自己的ImageButton 在编写我们自己的ImageButton之前,如果读者并不清楚如何在一个静态的xml布局文件中动态地加载子布局,请先阅读下面的博文(此文言简意赅,已经写得很清楚了,bill就不再赘述) https://www.sodocs.net/doc/3f18806220.html,/lzx_bupt/article/details/5600187首先,我们编写一个MyImageButton类,继承自LinearLayout 第一类:属性值为true或false android:layout_centerHrizontal 水平居中 android:layout_centerVertical 垂直居中 android:layout_centerInparent 相对于父元素完全居中 android:layout_alignParentBottom 贴紧父元素的下边缘 android:layout_alignParentLeft 贴紧父元素的左边缘 android:layout_alignParentRight 贴紧父元素的右边缘 android:layout_alignParentTop 贴紧父元素的上边缘 android:layout_alignWithParentIfMissing 如果对应的兄弟元素找不到的话就以父元素做参照物 第二类:属性值必须为id的引用名“@id/id-name” android:layout_below 在某元素的下方 android:layout_above 在某元素的的上方 android:layout_toLeftOf 在某元素的左边 android:layout_toRightOf 在某元素的右边 android:layout_alignTop 本元素的上边缘和某元素的的上边缘对齐 android:layout_alignLeft 本元素的左边缘和某元素的的左边缘对齐 android:layout_alignBottom 本元素的下边缘和某元素的的下边缘对齐 android:layout_alignRight 本元素的右边缘和某元素的的右边缘对齐 第三类:属性值为具体的像素值,如30dip,40px android:layout_marginBottom 离某元素底边缘的距离 android:layout_marginLeft 离某元素左边缘的距离 android:layout_marginRight 离某元素右边缘的距离 android:layout_marginTop 离某元素上边缘的距离 EditText的android:hint 设置EditText为空时输入框内的提示信息。 花了一天时间写出了这个类来实现下拉刷新。 首先这是一个自定义的listView控件类,我在许多项目中都用到了它,效果很稳定。实现也很简单。用的时候其他功能都和系统提供的ListView一样,就只是多了一个下拉刷新监听。用这个类替代系统提供的ListView,下拉刷新再也不会烦恼了。\(^o^)/~ 希望对大家有用。 我写的自定义ListView的代码: import java.util.Date; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import https://www.sodocs.net/doc/3f18806220.html,youtInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.LinearInterpolator; import android.view.animation.RotateAnimation; import android.widget.AbsListView; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.AbsListView.OnScrollListener; import android.widget.ProgressBar; import android.widget.TextView; public class MyListView extends ListView implements OnScrollListener { private static final String TAG = "listview"; private final static int RELEASE_To_REFRESH = 0; private final static int PULL_To_REFRESH = 1; private final static int REFRESHING = 2; private final static int DONE = 3; private final static int LOADING = 4; // 实际的padding的距离与界面上偏移距离的比例 private final static int RATIO = 3; private LayoutInflater inflater; private LinearLayout headView; private TextView tipsTextview;android 自定义圆角头像以及使用declare-styleable进行配置属性解析
android studio 控件常用属性
Android平台我的日记设计文档
Android常用控件
android 自定义控件的过程
android常用控件大全
Android进阶——自定义View之自己绘制彩虹圆环调色板
android自定义布局或View
Android自定义控件
实验二 android基本控件实验
android自定义View之Android手机通讯录制作
Android自定义View
Android基础-控件的使用
基于Android的自定义媒体播放控件设计与实现
Android应用开发基础习题
自定义Android带图片的按钮
android 控件常用属性
Android中自定义ListView控件实现下拉刷新,简单实用效果好(原创)