标签归档:View

android 设置EditText光标位置

Android中有很多可编辑的弹出框,其中有些是让我们来修改其中的字符,这时光标位置定位在哪里呢?
刚刚解了一个bug是关于这个光标的位置的,似乎Android原生中这种情况是把光标定位到字符串的最前面。需求是将光标定位到字符的最后面。
修改的地方是TextView这个控件,因为EditText也是继承了TextView。在setText方法中有:

1  private void setText(CharSequence text, BufferType type,
2                          boolean notifyBefore, int oldlen) {
3 ……
4         if (text instanceof Spannable) {
5             Spannable sp = (Spannable) text;
6
7             ……
8             if (mMovement != null) {
9                 mMovement.initialize(this, (Spannable) text);
10         //文本是不是Editable的。
11         if(this instanceof Editable)
12                      //设定光标位置
13                      Selection.setSelection((Spannable)text, text.length());
14
15                ……
16     }

从红色代码中可以看出,google是要光标处在缺省文本的末端,但是,log发现 (this instanceof Editable)非真,也就是说Selection.setSelection((Spannable)text, text.length());并不会被执行。

1    Log.d(“TextView”, “(type == BufferType.EDITABLE)=”+(type == BufferType.EDITABLE));
2    if(type == BufferType.EDITABLE){
3          Log.d(“TextView”,”Format text.Set cursor to the end “);
4          Selection.setSelection((Spannable)text, text.length());
5    }

这个样修改后即可。

 

在编写应用的时候,如果我们要将光标定位到某个位置,可以采用下面的方法:

1 CharSequence text = editText.getText();
2 //Debug.asserts(text instanceof Spannable);
3 if (text instanceof Spannable) {
4     Spannable spanText = (Spannable)text;
5     Selection.setSelection(spanText, text.length());
6 }

其中红色标记的代码为你想要设置的位置,此处是设置到文本末尾。

android declare-styleable中format 参考说明

我们在做项目的时候,由于Android自带的属性不能满足需求,android提供了自定义属性的方法,其中的format是做什么用的?以及如何使用它?下面列出一些常用的。

  • reference:参考某一资源ID。
  • color:颜色值。
  • boolean:布尔值。
  • dimension:尺寸值。
  • float:浮点值。
  • integer:整型值。
  • string:字符串。
  • fraction:百分数。
  • enum:枚举值。
  • flag:位或运算。

具体示例如下:

  • reference:参考某一资源ID。
    1. 属性定义:
    <declare-styleable name = "名称">
        <attr name = "background" format = "reference" />
    </declare-styleable>
    

    2.属性使用:

    <ImageView
        android:layout_width = "42dip"
        android:layout_height = "42dip"
        android:background = "@drawable/图片ID"
    />
    
  • color:颜色值。
    1. 属性定义:
    <declare-styleable name = "名称">
        <attr name = "textColor" format = "color" />
    </declare-styleable>
    

    2.属性使用:

    <TextView
        android:layout_width = "42dip"
        android:layout_height = "42dip"
        android:textColor = "#00FF00"
    />
    
  • boolean:布尔值。
    1. 属性定义:
    <declare-styleable name = "名称">
         <attr name = "focusable" format = "boolean" />
    </declare-styleable>
    

    2. 属性使用:

      <Button
      android:layout_width = "42dip"
      android:layout_height = "42dip"
      android:focusable = "true"
      />
    
  • dimension:尺寸值。
    1. 属性定义:
    <declare-styleable name = "名称">
        <attr name = "layout_width" format = "dimension" />
    </declare-styleable>
    

    2.属性使用:

    <Button
            android:layout_width = "42dip"
            android:layout_height = "42dip"
    />
    
  • float:浮点值。
    1. 属性定义:
    <declare-styleable name = "AlphaAnimation">
        <attr name = "fromAlpha" format = "float" />
        <attr name = "toAlpha" format = "float" />
    </declare-styleable>
    

    2.属性使用:

    <alpha 
        android:fromAlpha = "1.0"
        android:toAlpha = "0.7"
    />
    
  • integer:整型值。
    1. 属性定义:
    <declare-styleable name = "AnimatedRotateDrawable">
        <attr name = "visible" />
        <attr name = "frameDuration" format="integer" />
        <attr name = "framesCount" format="integer" />
        <attr name = "pivotX" />
        <attr name = "pivotY" />
        <attr name = "drawable" />
    </declare-styleable>
    

    2.属性使用:

    <animated-rotate
        xmlns:android = "http://schemas.android.com/apk/res/android" 
         android:drawable = "@drawable/图片ID" 
         android:pivotX = "50%" 
         android:pivotY = "50%" 
         android:framesCount = "12" 
         android:frameDuration = "100"
      />
    

     

  • string:字符串。
    1. 属性定义:
      <declare-styleable name = "MapView">
            <attr name = "apiKey" format = "string" />
      </declare-styleable>
    

    2.属性使用:

    <com.google.android.maps.MapView
            android:layout_width = "fill_parent"
         android:layout_height = "fill_parent"
         android:apiKey = "0jOkQ80oD1JL9C6HAja99uGXCRiS2CGjKO_bc_g"
     />
    
  • fraction:百分数。
    1. 属性定义:
      <declare-styleable name="RotateDrawable">
            <attr name = "visible" />
            <attr name = "fromDegrees" format = "float" />
            <attr name = "toDegrees" format = "float" />        <attr name = "pivotX" format = "fraction" />
            <attr name = "pivotY" format = "fraction" />
         <attr name = "drawable" />
      </declare-styleable>
    

    2.属性使用:

    <rotate
      xmlns:android = "http://schemas.android.com/apk/res/android"
        android:interpolator = "@anim/动画ID"
        android:fromDegrees = "0"
        android:toDegrees = "360"
        android:pivotX = "200%"
        android:pivotY = "300%"
        android:duration = "5000"
        android:repeatMode = "restart"
        android:repeatCount = "infinite"
        />
    
  • enum:枚举值。
    1. 属性定义:
    <declare-styleable name="名称">
        <attr name="orientation">
        <enum name="horizontal" value="0" />
        <enum name="vertical" value="1" />
        </attr>           
    </declare-styleable>
    

    2.属性使用:

    <LinearLayout
        xmlns:android = "http://schemas.android.com/apk/res/android"
        android:orientation = "vertical"
        android:layout_width = "fill_parent"
        android:layout_height = "fill_parent"
        >
    </LinearLayout>
    
  • flag:位或运算。
    1. 属性定义:
    <declare-styleable name="名称">
         <attr name="windowSoftInputMode">
         <flag name = "stateUnspecified" value = "0" />
         <flag name = "stateUnchanged" value = "1" />
         <flag name = "stateHidden" value = "2" />
         <flag name = "stateAlwaysHidden" value = "3" />
         <flag name = "stateVisible" value = "4" />
         <flag name = "stateAlwaysVisible" value = "5" />
         <flag name = "adjustUnspecified" value = "0x00" />
         <flag name = "adjustResize" value = "0x10" />
         <flag name = "adjustPan" value = "0x20" />
         <flag name = "adjustNothing" value = "0x30" />
         </attr>        
    </declare-styleable>
    

    2.属性使用:

    <activity
        android:name = ".StyleAndThemeActivity"
        android:label = "@string/app_name"
        android:windowSoftInputMode = "stateUnspecified | stateUnchanged | stateHidden">
        <intent-filter>
            <action android:name = "android.intent.action.MAIN" />
            <category android:name = "android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    

特别要注意:属性定义时可以指定多种类型值。
1. 属性定义:

<declare-styleable name = "名称">
    <attr name = "background" format = "reference|color" />
</declare-styleable>

2.属性使用:

<ImageView
android:layout_width = "42dip"
android:layout_height = "42dip"
android:background = "@drawable/图片ID|#00FF00"
/>

 

 

Android开发示例:如何设置圆角的EditText

设置EditText边框为圆角如下图所示:

首先定义一个圆角xml,路径:res/drawable/rounded_edittext.xml

  1. <?xml version=“1.0” encoding=“utf-8”?>
  2. <shape xmlns:Android=“http://schemas.android.com/apk/res/android”
  3.     android:shape=“rectangle” android:padding=“10dp”>
  4.     <solid android:color=“#FFFFFF” />
  5.     <corners android:bottomRightRadius=“15dp”
  6.         android:bottomLeftRadius=“15dp” android:topLeftRadius=“15dp”
  7.         android:topRightRadius=“15dp” />
  8. </shape>

显示控件的xml中

  1. <EditText android:id=“@+id/edt_operator_name” style=“@style/syncEditText”
  2.                     android:hint=“@string/hint_operator_name”
  3. android:background=“@drawable/rounded_edittext”
  4. android:layout_width=“250dip”
  5. android:layout_height=“wrap_content”
  6. android:paddingTop=“10dip”
  7. android:paddingBottom=“10dip”
  8. android:gravity=“center_vertical” />

其中关键的就是设置编辑文本控件的background属性为刚定义的圆角xml。

即:

  1. android:background=“@drawable/rounded_edittext”

 

Android自定义Dialog

这段时间在做一个项目,需要使用到自定义Dialog,先在网上找了一下资料,发现还是有很多没有讲清楚的,在此给出一个Demo,一来可以方便广大码农,二来也可以方便自己,以备不时之需。。。

先来一张图吧,很简单,只有一个Activity,当点击Button的时候就弹出这个自定义的Dialog

里面的几张图都比较丑,我不多会美工,随便用powerpoint画了几张图,原理是一样的,先不计较这些。下面正入正题

为了照顾到所有的码农,在些把所有的代码都贴出来

新建工程在此就不贴出来了,只是为了方便大家的复制粘贴,取包名为com.and.mydialog,主Activity取名为MyDialogActivity

package com.and.mydialog;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class MyDialogActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Button button = (Button) findViewById(R.id.button1);
        button.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                
                //初始化一个自定义的Dialog
                Dialog dialog = new MyDialog(MyDialogActivity.this,
                        R.style.MyDialog);

                dialog.show();
            }
        });

    }
}

主布局文件main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <Button 
        android:text="显示自定义Dialog" 
        android:id="@+id/button1"
        android:layout_height="wrap_content" 
        android:layout_width="fill_parent"/>
</LinearLayout>

新建一个自定义的Dialog类,取名MyDialog,继承自Dialog

package com.and.mydialog;

import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;

public class MyDialog extends Dialog {

    Context context;
    public MyDialog(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        this.context = context;
    }
    public MyDialog(Context context, int theme){
        super(context, theme);
        this.context = context;
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        this.setContentView(R.layout.dialog);
    }

}

相应的布局文件dialog.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="center_vertical|center_horizontal"
    android:background="@drawable/dialog_bg">
    <RelativeLayout 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="30dip"
        android:paddingTop="10dip">
        <ImageView 
            android:id="@+id/dialog_title_image"
            android:layout_alignParentLeft="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/dialog_title_image"/>
        <TextView 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dip"
            android:layout_centerInParent="true"
            android:text="Title"
            android:layout_toRightOf="@id/dialog_title_image"
            android:textColor="#000000"
            android:textSize="30sp"/>
        
    </RelativeLayout>
    <TextView 
            android:layout_width="fill_parent"
            android:layout_height="1dip"
            android:background="@drawable/lins"
            android:layout_marginTop="5dip"/>
    <TextView 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="This is a custom dialog"
        android:textColor="#000000"
        android:layout_marginTop="10dip"
        android:layout_marginLeft="30dip"/>
    <RelativeLayout 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingTop="10dip"
        android:gravity="bottom|center_horizontal"
        android:paddingBottom="10dip">
        <Button 
            android:id="@+id/dialog_button_cancel"
            android:layout_alignParentLeft="true"
            android:layout_width="100dip"
            android:layout_height="wrap_content"
            android:text="确定"/>
        <Button 
            android:id="@+id/dialog_button_ok"
            android:layout_width="100dip"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@id/dialog_button_cancel"
            android:layout_marginLeft="35dip"
            android:text="取消"/>
    </RelativeLayout>
</LinearLayout>

最主要的,是自定义的Style,我们自定义一个式样,用来改变默认的Dialog样式

在values文件夹下新建一个styles.xml文件,如下。。。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="MyDialog" parent="@android:Theme.Dialog">
        <item name="android:windowFrame">@null</item>
        <item name="android:windowNoTitle">true</item> 
        <item name="android:windowBackground">@drawable/dialog_bg</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowContentOverlay">@null</item>
    </style>
</resources>

这样应该就OK了,为了方便大家测试本示例,在此一并附上不怎么好看的素材。。。

注意,这里有三张图片,第三张图片是一条线,在自定义的式样的时候取消了标题栏,为了美观,添加此线条,看上去还是有标题栏的感觉。。。在此基本上完成了。

由于水平有限,这些内容基本上都是在网上找的,然后自己整理了一下,写了一篇相对比较清晰的,如果大家还有什么疑问的话,随时可以跟我联系,共同学习。。。

———————————————————————————忧伤的分割线——————————————————————————

不好意思,刚刚实测了一下,发现还有一个很简单的方法,在不改变前面的布局前提下,只需要修改主类(MyDialogActivity.java)

package com.and.mydialog;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class MyDialogActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Button button = (Button) findViewById(R.id.button1);
        button.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {

//                Dialog dialog = new MyDialog(MyDialogActivity.this,
//                        R.style.MyDialog);
                //此处直接new一个Dialog对象出来,在实例化的时候传入主题
                Dialog dialog = new Dialog(MyDialogActivity.this, R.style.MyDialog);
 //设置它的ContentView
                dialog.setContentView(R.layout.dialog);

                dialog.show();
            }
        });

    }
}

Android Animation总结

3.0以前,android支持两种动画模式,tween animation,frame animation,在android3.0中又引入了一个新的动画系统:property animation,这三种动画模式在SDK中被称为property animation,view animation,drawable animation。 可通过NineOldAndroids项目在3.0之前的系统中使用Property Animation

1. View Animation(Tween Animation)

View Animation(Tween Animation):补间动画,给出两个关键帧,通过一些算法将给定属性值在给定的时间内在两个关键帧间渐变。

View animation只能应用于View对象,而且只支持一部分属性,如支持缩放旋转而不支持背景颜色的改变。

而且对于View animation,它只是改变了View对象绘制的位置,而没有改变View对象本身,比如,你有一个Button,坐标(100,100),Width:200,Height:50,而你有一个动画使其变为Width:100,Height:100,你会发现动画过程中触发按钮点击的区域仍是(100,100)-(300,150)。

View Animation就是一系列View形状的变换,如大小的缩放,透明度的改变,位置的改变,动画的定义既可以用代码定义也可以用XML定义,当然,建议用XML定义。

可以给一个View同时设置多个动画,比如从透明至不透明的淡入效果,与从小到大的放大效果,这些动画可以同时进行,也可以在一个完成之后开始另一个。

用XML定义的动画放在/res/anim/文件夹内,XML文件的根元素可以为<alpha>,<scale>,<translate>,<rotate>,interpolator元素或<set>(表示以上几个动画的集合,set可以嵌套)。默认情况下,所有动画是同时进行的,可以通过startOffset属性设置各个动画的开始偏移(开始时间)来达到动画顺序播放的效果。

可以通过设置interpolator属性改变动画渐变的方式,如AccelerateInterpolator,开始时慢,然后逐渐加快。默认为AccelerateDecelerateInterpolator。

定义好动画的XML文件后,可以通过类似下面的代码对指定View应用动画。

ImageView spaceshipImage = (ImageView)findViewById(R.id.spaceshipImage);
Animation hyperspaceJumpAnimation=AnimationUtils.loadAnimation(this, R.anim.hyperspace_jump);
spaceshipImage.startAnimation(hyperspaceJumpAnimation);

2. Drawable Animation(Frame Animation)

Drawable Animation(Frame Animation):帧动画,就像GIF图片,通过一系列Drawable依次显示来模拟动画的效果。在XML中的定义方式如下:

复制代码
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="true">
    <item android:drawable="@drawable/rocket_thrust1" android:duration="200" />
    <item android:drawable="@drawable/rocket_thrust2" android:duration="200" />
    <item android:drawable="@drawable/rocket_thrust3" android:duration="200" />
</animation-list>
复制代码

必须以<animation-list>为根元素,以<item>表示要轮换显示的图片,duration属性表示各项显示的时间。XML文件要放在/res/drawable/目录下。示例:

复制代码
protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        imageView = (ImageView) findViewById(R.id.imageView1);
        imageView.setBackgroundResource(R.drawable.drawable_anim);
        anim = (AnimationDrawable) imageView.getBackground();
    }

    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            anim.stop();
            anim.start();
            return true;
        }
        return super.onTouchEvent(event);
    }
复制代码

我在实验中遇到两点问题:

  1. 要在代码中调用Imageview的setBackgroundResource方法,如果直接在XML布局文件中设置其src属性当触发动画时会FC。
  2. 在动画start()之前要先stop(),不然在第一次动画之后会停在最后一帧,这样动画就只会触发一次。
  3. 最后一点是SDK中提到的,不要在onCreate中调用start,因为AnimationDrawable还没有完全跟Window相关联,如果想要界面显示时就开始动画的话,可以在onWindowFoucsChanged()中调用start()。

3. Property Animation

属性动画,这个是在Android 3.0中才引进的,以前学WPF时里面的动画机制好像就是这个,它更改的是对象的实际属性,在View Animation(Tween Animation)中,其改变的是View的绘制效果,真正的View的属性保持不变,比如无论你在对话中如何缩放Button的大小,Button的有效点击区域还是没有应用动画时的区域,其位置与大小都不变。而在Property Animation中,改变的是对象的实际属性,如Button的缩放,Button的位置与大小属性值都改变了。而且Property Animation不止可以应用于View,还可以应用于任何对象。Property Animation只是表示一个值在一段时间内的改变,当值改变时要做什么事情完全是你自己决定的。

在Property Animation中,可以对动画应用以下属性:

  • Duration:动画的持续时间
  • TimeInterpolation:属性值的计算方式,如先快后慢
  • TypeEvaluator:根据属性的开始、结束值与TimeInterpolation计算出的因子计算出当前时间的属性值
  • Repeat Count and behavoir:重复次数与方式,如播放3次、5次、无限循环,可以此动画一直重复,或播放完时再反向播放
  • Animation sets:动画集合,即可以同时对一个对象应用几个动画,这些动画可以同时播放也可以对不同动画设置不同开始偏移
  • Frame refreash delay:多少时间刷新一次,即每隔多少时间计算一次属性值,默认为10ms,最终刷新时间还受系统进程调度与硬件的影响

3.1 Property Animation的工作方式

对于下图的动画,这个对象的X坐标在40ms内从0移动到40 pixel.按默认的10ms刷新一次,这个对象会移动4次,每次移动40/4=10pixel。

也可以改变属性值的改变方法,即设置不同的interpolation,在下图中运动速度先逐渐增大再逐渐减小

下图显示了与上述动画相关的关键对象

ValueAnimator  表示一个动画,包含动画的开始值,结束值,持续时间等属性。

ValueAnimator封装了一个TimeInterpolator,TimeInterpolator定义了属性值在开始值与结束值之间的插值方法。

ValueAnimator还封装了一个TypeAnimator,根据开始、结束值与TimeIniterpolator计算得到的值计算出属性值。

ValueAnimator根据动画已进行的时间跟动画总时间(duration)的比计算出一个时间因子(0~1),然后根据TimeInterpolator计算出另一个因子,最后TypeAnimator通过这个因子计算出属性值,如上例中10ms时:

首先计算出时间因子,即经过的时间百分比:t=10ms/40ms=0.25

经插值计算(inteplator)后的插值因子:大约为0.15,上述例子中用了AccelerateDecelerateInterpolator,计算公式为(input即为时间因子):

(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;

最后根据TypeEvaluator计算出在10ms时的属性值:0.15*(40-0)=6pixel。上例中TypeEvaluator为FloatEvaluator,计算方法为 :

public Float evaluate(float fraction, Number startValue, Number endValue) {
    float startFloat = startValue.floatValue();
    return startFloat + fraction * (endValue.floatValue() - startFloat);
}

参数分别为上一步的插值因子,开始值与结束值。

3.2 ValueAnimator

ValueAnimator包含Property Animation动画的所有核心功能,如动画时间,开始、结束属性值,相应时间属性值计算方法等。应用Property Animation有两个步聚:

  1. 计算属性值
  2. 根据属性值执行相应的动作,如改变对象的某一属性。

ValuAnimiator只完成了第一步工作,如果要完成第二步,需要实现ValueAnimator.onUpdateListener接口,这个接口只有一个函数onAnimationUpdate(),在这个函数中会传入ValueAnimator对象做为参数,通过这个ValueAnimator对象的getAnimatedValue()函数可以得到当前的属性值如:

复制代码
ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
animation.setDuration(1000);
animation.addUpdateListener(new AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        Log.i("update", ((Float) animation.getAnimatedValue()).toString());
    }
});
animation.setInterpolator(new CycleInterpolator(3));
animation.start();
复制代码

此示例中只是向Logcat输出了一些信息,可以改为想做的工作。

Animator.AnimatorListener

复制代码
onAnimationStart()

onAnimationEnd()

onAnimationRepeat()

//当动画被取消时调用,同时会调用onAnimationEnd().
onAnimationCancel()
复制代码

ValueAnimator.AnimatorUpdateListener

onAnimationUpdate()  //通过监听这个事件在属性的值更新时执行相应的操作,对于ValueAnimator一般要监听此事件执行相应的动作,不然Animation没意义,在ObjectAnimator(继承自ValueAnimator)中会自动更新属性,如无必要不必监听。在函数中会传递一个ValueAnimator参数,通过此参数的getAnimatedValue()取得当前动画属性值。

可以继承AnimatorListenerAdapter而不是实现AnimatorListener接口来简化操作,这个类对AnimatorListener中的函数都定义了一个空函数体,这样我们就只用定义想监听的事件而不用实现每个函数却只定义一空函数体。

复制代码
ObjectAnimator oa=ObjectAnimator.ofFloat(tv, "alpha", 0f, 1f);
oa.setDuration(3000);
oa.addListener(new AnimatorListenerAdapter(){
    public void on AnimationEnd(Animator animation){
        Log.i("Animation","end");
    }
});
oa.start();
复制代码

3.3 ObjectAnimator

继承自ValueAnimator,要指定一个对象及该对象的一个属性,当属性值计算完成时自动设置为该对象的相应属性,即完成了Property Animation的全部两步操作。实际应用中一般都会用ObjectAnimator来改变某一对象的某一属性,但用ObjectAnimator有一定的限制,要想使用ObjectAnimator,应该满足以下条件:

  • 对象应该有一个setter函数:set<PropertyName>(驼峰命名法)
  • 如上面的例子中,像ofFloat之类的工场方法,第一个参数为对象名,第二个为属性名,后面的参数为可变参数,如果values…参数只设置了一个值的话,那么会假定为目的值,属性值的变化范围为当前值到目的值,为了获得当前值,该对象要有相应属性的getter方法:get<PropertyName>
  • 如果有getter方法,其应返回值类型应与相应的setter方法的参数类型一致。

如果上述条件不满足,则不能用ObjectAnimator,应用ValueAnimator代替。

复制代码
tv=(TextView)findViewById(R.id.textview1);
btn=(Button)findViewById(R.id.button1);
btn.setOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
    ObjectAnimator oa=ObjectAnimator.ofFloat(tv, "alpha", 0f, 1f);
    oa.setDuration(3000);
    oa.start();
  }
});
复制代码

把一个TextView的透明度在3秒内从0变至1。

根据应用动画的对象或属性的不同,可能需要在onAnimationUpdate函数中调用invalidate()函数刷新视图。

3.4 通过AnimationSet应用多个动画

AnimationSet提供了一个把多个动画组合成一个组合的机制,并可设置组中动画的时序关系,如同时播放,顺序播放等。

以下例子同时应用5个动画:

  1. 播放anim1;
  2. 同时播放anim2,anim3,anim4;
  3. 播放anim5。
AnimatorSet bouncer = new AnimatorSet();
bouncer.play(anim1).before(anim2);
bouncer.play(anim2).with(anim3);
bouncer.play(anim2).with(anim4)
bouncer.play(anim5).after(amin2);
animatorSet.start();

3.5 TypeEvalutors

根据属性的开始、结束值与TimeInterpolation计算出的因子计算出当前时间的属性值,android提供了以下几个evalutor:

  • IntEvaluator:属性的值类型为int;
  • FloatEvaluator:属性的值类型为float;
  • ArgbEvaluator:属性的值类型为十六进制颜色值;
  • TypeEvaluator:一个接口,可以通过实现该接口自定义Evaluator。

自定义TypeEvalutor很简单,只需要实现一个方法,如FloatEvalutor的定义:

复制代码
public class FloatEvaluator implements TypeEvaluator {
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        float startFloat = ((Number) startValue).floatValue();
        return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
    }
}
复制代码

根据动画执行的时间跟应用的Interplator,会计算出一个0~1之间的因子,即evalute函数中的fraction参数,通过上述FloatEvaluator应该很好看出其意思。

3.6 TimeInterplator

Time interplator定义了属性值变化的方式,如线性均匀改变,开始慢然后逐渐快等。在Property Animation中是TimeInterplator,在View Animation中是Interplator,这两个是一样的,在3.0之前只有Interplator,3.0之后实现代码转移至了TimeInterplator。Interplator继承自TimeInterplator,内部没有任何其他代码。

  • AccelerateInterpolator          加速,开始时慢中间加速
  • DecelerateInterpolator         减速,开始时快然后减速
  • AccelerateDecelerateInterolator    先加速后减速,开始结束时慢,中间加速
  • AnticipateInterpolator        反向 ,先向相反方向改变一段再加速播放
  • AnticipateOvershootInterpolator    反向加回弹,先向相反方向改变,再加速播放,会超出目的值然后缓慢移动至目的值
  • BounceInterpolator         跳跃,快到目的值时值会跳跃,如目的值100,后面的值可能依次为85,77,70,80,90,100
  • CycleIinterpolator         循环,动画循环一定次数,值的改变为一正弦函数:Math.sin(2 * mCycles * Math.PI * input)
  • LinearInterpolator         线性,线性均匀改变
  • OvershottInterpolator        回弹,最后超出目的值然后缓慢改变到目的值
  • TimeInterpolator           一个接口,允许你自定义interpolator,以上几个都是实现了这个接口

3.7 当Layout改变时应用动画

ViewGroup中的子元素可以通过setVisibility使其Visible、Invisible或Gone,当有子元素可见性改变时(VISIBLE、GONE),可以向其应用动画,通过LayoutTransition类应用此类动画:

transition.setAnimator(LayoutTransition.DISAPPEARING, customDisappearingAnim);

通过setAnimator应用动画,第一个参数表示应用的情境,可以以下4种类型:

  • APPEARING        当一个元素在其父元素中变为Visible时对这个元素应用动画
  • CHANGE_APPEARING    当一个元素在其父元素中变为Visible时,因系统要重新布局有一些元素需要移动,对这些要移动的元素应用动画
  • DISAPPEARING       当一个元素在其父元素中变为GONE时对其应用动画
  • CHANGE_DISAPPEARING   当一个元素在其父元素中变为GONE时,因系统要重新布局有一些元素需要移动,这些要移动的元素应用动画.

第二个参数为一Animator。

mTransitioner.setStagger(LayoutTransition.CHANGE_APPEARING, 30);

此函数设置动画延迟时间,参数分别为类型与时间。

3.8 Keyframes

keyFrame是一个 时间/值 对,通过它可以定义一个在特定时间的特定状态,即关键帧,而且在两个keyFrame之间可以定义不同的Interpolator,就好像多个动画的拼接,第一个动画的结束点是第二个动画的开始点。KeyFrame是抽象类,要通过ofInt(),ofFloat(),ofObject()获得适当的KeyFrame,然后通过PropertyValuesHolder.ofKeyframe获得PropertyValuesHolder对象,如以下例子:

复制代码
Keyframe kf0 = Keyframe.ofInt(0, 400);
Keyframe kf1 = Keyframe.ofInt(0.25f, 200);
Keyframe kf2 = Keyframe.ofInt(0.5f, 400);
Keyframe kf4 = Keyframe.ofInt(0.75f, 100);
Keyframe kf3 = Keyframe.ofInt(1f, 500);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("width", kf0, kf1, kf2, kf4, kf3);
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(btn2, pvhRotation);
rotationAnim.setDuration(2000);
复制代码

上述代码的意思为:设置btn对象的width属性值使其:

  • 开始时 Width=400
  • 动画开始1/4时 Width=200
  • 动画开始1/2时 Width=400
  • 动画开始3/4时 Width=100
  • 动画结束时 Width=500
第一个参数为时间百分比,第二个参数是在第一个参数的时间时的属性值。
定义了一些Keyframe后,通过PropertyValuesHolder类的方法ofKeyframe一个PropertyValuesHolder对象,然后通过ObjectAnimator.ofPropertyValuesHolder获得一个Animator对象。
用下面的代码可以实现同样的效果(上述代码时间值是线性,变化均匀):
ObjectAnimator oa=ObjectAnimator.ofInt(btn2, "width", 400,200,400,100,500);
oa.setDuration(2000);
oa.start();

3.9 Animating Views

在View Animation中,对View应用Animation并没有改变View的属性,动画的实现是通过其Parent View实现的,在View被drawn时Parents View改变它的绘制参数,draw后再改变参数invalidate,这样虽然View的大小或旋转角度等改变了,但View的实际属性没变,所以有效区域还是应用动画之前的区域,比如你把一按钮放大两倍,但还是放大这前的区域可以触发点击事件。为了改变这一点,在Android 3.0中给View增加了一些参数并对这些参数增加了相应的getter/setter函数(ObjectAnimator要用这些函数改变这些属性):

  • translationX,translationY: View相对于原始位置的偏移量
  • rotation,rotationX,rotationY: 旋转,rotation用于2D旋转角度,3D中用到后两个
  • scaleX,scaleY: 缩放比
  • x,y: View的最终坐标,是View的left,top位置加上translationX,translationY
  • alpha: 透明度
跟位置有关的参数有3个,以X坐标为例,可以通过getLeft(),getX(),getTranslateX()获得,若有一Button btn2,布局时其坐标为(40,0):
复制代码
//应用动画之前
btn2.getLeft();    //40
btn2.getX();    //40
btn2.getTranslationX();    //0
//应用translationX动画
ObjectAnimator oa=ObjectAnimator.ofFloat(btn2,"translationX", 200);
oa.setDuration(2000);
oa.start();
/*应用translationX动画后
btn2.getLeft();    //40
btn2.getX();    //240
btn2.getTranslationX();    //200
*/
//应用X动画,假设没有应用之前的translationX动画
ObjectAnimator oa=ObjectAnimator.ofFloat(btn2, "x", 200);
oa.setDuration(2000);
oa.start();
/*应用X动画后
btn2.getLeft();    //40
btn2.getX();    //200
btn2.getTranslationX();    //160
*/
复制代码
无论怎样应用动画,原来的布局时的位置通过getLeft()获得,保持不变;
  X是View最终的位置;
  translationX为最终位置与布局时初始位置这差。
  所以若就用translationX即为在原来基础上移动多少,X为最终多少
  getX()的值为getLeft()与getTranslationX()的和
  对于X动画,源代码是这样的:
case X:
       info.mTranslationX = value - mView.mLeft;
       break;

Property Animation也可以在XML中定义

  • <set> – AnimatorSet
  • <animator> – ValueAnimator
  • <objectAnimator> – ObjectAnimator
XML文件应放大/res/animator/中,通过以下方式应用动画:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext, R.anim.property_animator);
set.setTarget(myObject);
set.start();

3.10 ViewPropertyAnimator

如果需要对一个View的多个属性进行动画可以用ViewPropertyAnimator类,该类对多属性动画进行了优化,会合并一些invalidate()来减少刷新视图,该类在3.1中引入。

以下两段代码实现同样的效果:

PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
myView.animate().x(50f).y(100f);

Android TextView 动态设置颜色

问题描述

最近写程序就遇到了这么个难题,在TextView 上,正常字体颜色显示是黑色。如果在activity中动态的改变字体颜色(预期目标是字体颜色变成红色),无论如何都是灰色,简单代码如下所示。郁闷了好些时日。

private TextView mTextDisp;
mTextDisp = (TextView) findViewById(R.id.textDisp_mian);
mTextDisp.setTextColor(R.color.red);//(使用color.xml文件中的颜色值)

这样写是怎么也变不成红色的,而且程序不报错,不知道朋友们有没有试过。而且debug所走的分支也是正确的。
我就单独写了一个Demo来测试,结果还是灰色。
有的朋友问,是不是red的颜色值写错了。不是,color中的颜色值配置对着呢。

解决

其实,答案很简单,就错在mTextDisp.setTextColor(R.color.red);这行代码上。

  1. 首先,在xml中不要写默认的字体颜色值,即android:textColor="xxx"
  2. 其次,在activity中mTextDisp.setTextColor(context.getResources().getColor(R.color.red));(使用color.xml文件中的颜色值)
    这样就OK了。或者直接使用Color类中的值:mTextDisp.setTextColor(Color.RED);(使用系统自带的颜色类Color类中的颜色值)
    或者直接使用颜色值:mTextDisp.setTextColor(0xffff00ff);//0xffff00ff是int类型的数据,分组一下0x|ff|ff00ff,0x是代表颜色整数的标记,ff是表示透明度,ff00ff表示颜色,注意:这里ffff00ff必须是8个的颜色表示,不接受ff00ff这种6个的颜色表示。

ps

点进去看下文档就知道了。需要的是颜色值,而R.color.red只是一个int。类似于一个key,value才是真正的颜色值。key->value表就是对应的xml文件。

Android中View绘制流程以及invalidate()等相关方法分析

转载请注明出处:http://blog.csdn.net/qinjuning

 前言: 本文是我读《Android内核剖析》第13章—-View工作原理总结而成的,在此膜拜下作者 。同时真挚地向渴望了解  Android 框架层网友,推荐这本书,希望你们能够在Android开发里学到更多的知识 。 

            整个View树的绘图流程是在ViewRoot.java类的performTraversals()函数展开的,该函数做的执行过程可简单概况为之前状态,判断是否需要重新计算视图大小(measure)、是否重新需要安置视图的位置(layout)、以及是否需要重绘 (draw),其框架过程如下:

                                                                                                   步骤其实为host.layout() 

           

 

      接下来温习一下整个View树的结构,对每个具体View对象的操作,其实就是个递归的实现。

 

 

   关于这个 DecorView 根视图的说明,可以参考我的这篇博客:

    《Android中将布局文件/View添加至窗口过程分析 —- 从setContentView()谈起》

 

  流程一:      mesarue()过程

        主要作用:为整个View树计算实际的大小,即设置实际的高(对应属性:mMeasuredHeight)和宽(对应属性:mMeasureWidth),每个View的控件的实际宽高都是由父视图和本身视图决定的。

     具体的调用链如下

          ViewRoot根对象的属性mView(其类型一般为ViewGroup类型)调用measure()方法去计算View树的大小,回调View/ViewGroup对象的onMeasure()方法,该方法实现的功能如下:    

         1、设置本View视图的最终大小,该功能的实现通过调用setMeasuredDimension()方法去设置实际的高(对应属性:  mMeasuredHeight)和宽(对应属性:mMeasureWidth)   ;

         2 、如果该View对象是个ViewGroup类型,需要重写该onMeasure()方法,对其子视图进行遍历的measure()过程。

                             2.1  对每个子视图的measure()过程,是通过调用父类ViewGroup.java类里的measureChildWithMargins()方法去 实现,该方法内部只是简单地调用了View对象的measure()方法。(由于measureChildWithMargins()方法只是一个过渡 层更简单的做法是直接调用View对象的measure()方法)。

      整个measure调用流程就是个树形的递归过程

  measure函数原型为 View.java 该函数不能被重载

  1. public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
  2.     //….
  3.     //回调onMeasure()方法  
  4.     onMeasure(widthMeasureSpec, heightMeasureSpec);
  5.     //more
  6. }

 

 为了大家更好的理解,采用“二B程序员”的方式利用伪代码描述该measure流程

 

  1. //回调View视图里的onMeasure过程
  2. private void onMeasure(int height , int width){
  3.  //设置该view的实际宽(mMeasuredWidth)高(mMeasuredHeight)
  4.  //1、该方法必须在onMeasure调用,否者报异常。
  5.  setMeasuredDimension(h , l) ;
  6.  //2、如果该View是ViewGroup类型,则对它的每个子View进行measure()过程
  7.  int childCount = getChildCount() ;
  8.  for(int i=0 ;i<childCount ;i++){
  9.   //2.1、获得每个子View对象引用
  10.   View child = getChildAt(i) ;
  11.   //整个measure()过程就是个递归过程
  12.   //该方法只是一个过滤器,最后会调用measure()过程 ;或者 measureChild(child , h, i)方法都
  13.   measureChildWithMargins(child , h, i) ;
  14.   //其实,对于我们自己写的应用来说,最好的办法是去掉框架里的该方法,直接调用view.measure(),如下:
  15.   //child.measure(h, l)
  16.  }
  17. }
  18. //该方法具体实现在ViewGroup.java里 。
  19. protected  void measureChildWithMargins(View v, int height , int width){
  20.  v.measure(h,l)
  21. }

 

流程二、 layout布局过程:

 

     主要作用 :为将整个根据子视图的大小以及布局参数将View树放到合适的位置上。

 

     具体的调用链如下:

       host.layout()开始View树的布局,继而回调给View/ViewGroup类中的layout()方法。具体流程如下

  

        1 、layout方法会设置该View视图位于父视图的坐标轴,即mLeft,mTop,mLeft,mBottom(调用setFrame()函数去实现)

  接下来回调onLayout()方法(如果该View是ViewGroup对象,需要实现该方法,对每个子视图进行布局) ;

       

       2、如果该View是个ViewGroup类型,需要遍历每个子视图chiildView,调用该子视图的layout()方法去设置它的坐标值。

 

layout函数原型为 ,位于View.java

  1. /* final 标识符 , 不能被重载 , 参数为每个视图位于父视图的坐标轴
  2.  * @param l Left position, relative to parent
  3.  * @param t Top position, relative to parent
  4.  * @param r Right position, relative to parent
  5.  * @param b Bottom position, relative to parent
  6.  */
  7. public final void layout(int l, int t, int r, int b) {
  8.     boolean changed = setFrame(l, t, r, b); //设置每个视图位于父视图的坐标轴
  9.     if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
  10.         if (ViewDebug.TRACE_HIERARCHY) {
  11.             ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);
  12.         }
  13.         onLayout(changed, l, t, r, b);//回调onLayout函数 ,设置每个子视图的布局
  14.         mPrivateFlags &= ~LAYOUT_REQUIRED;
  15.     }
  16.     mPrivateFlags &= ~FORCE_LAYOUT;
  17. }

 

 同样地, 将上面layout调用流程,用伪代码描述如下: 

  1. // layout()过程  ViewRoot.java
  2. // 发起layout()的”发号者”在ViewRoot.java里的performTraversals()方法, mView.layout()
  3. private void  performTraversals(){
  4.     //…
  5.     View mView  ;
  6.        mView.layout(left,top,right,bottom) ;
  7.     //….
  8. }
  9. //回调View视图里的onLayout过程 ,该方法只由ViewGroup类型实现
  10. private void onLayout(int left , int top , right , bottom){
  11.  //如果该View不是ViewGroup类型
  12.  //调用setFrame()方法设置该控件的在父视图上的坐标轴
  13.  setFrame(l ,t , r ,b) ;
  14.  //————————–
  15.  //如果该View是ViewGroup类型,则对它的每个子View进行layout()过程
  16.  int childCount = getChildCount() ;
  17.  for(int i=0 ;i<childCount ;i++){
  18.   //2.1、获得每个子View对象引用
  19.   View child = getChildAt(i) ;
  20.   //整个layout()过程就是个递归过程
  21.   child.layout(l, t, r, b) ;
  22.  }
  23. }

 

 

   流程三、 draw()绘图过程

     由ViewRoot对象的performTraversals()方法调用draw()方法发起绘制该View树,值得注意的是每次发起绘图时,并不 会重新绘制每个View树的视图,而只会重新绘制那些“需要重绘”的视图,View类内部变量包含了一个标志位DRAWN,当该视图需要重绘时,就会为该View添加该标志位。

 

   调用流程 :

     mView.draw()开始绘制,draw()方法实现的功能如下:

          1 、绘制该View的背景

          2 、为显示渐变框做一些准备操作(见5,大多数情况下,不需要改渐变框)          

          3、调用onDraw()方法绘制视图本身   (每个View都需要重载该方法,ViewGroup不需要实现该方法)

          4、调用dispatchDraw ()方法绘制子视图(如果该View类型不为ViewGroup,即不包含子视图,不需要重载该方法)

值得说明的是,ViewGroup类已经为我们重写了dispatchDraw ()的功能实现,应用程序一般不需要重写该方法,但可以重载父类

  函数实现具体的功能。

 

            4.1 dispatchDraw()方法内部会遍历每个子视图,调用drawChild()去重新回调每个子视图的draw()方法(注意,这个 

地方“需要重绘”的视图才会调用draw()方法)。值得说明的是,ViewGroup类已经为我们重写了dispatchDraw()的功能

实现,应用程序一般不需要重写该方法,但可以重载父类函数实现具体的功能。

    

     5、绘制滚动条

 

  于是,整个调用链就这样递归下去了。

    

     同样地,使用伪代码描述如下:

 

  1. // draw()过程     ViewRoot.java
  2. // 发起draw()的”发号者”在ViewRoot.java里的performTraversals()方法, 该方法会继续调用draw()方法开始绘图
  3. private void  draw(){
  4.     //…
  5.  View mView  ;
  6.     mView.draw(canvas) ;
  7.     //….
  8. }
  9. //回调View视图里的onLayout过程 ,该方法只由ViewGroup类型实现
  10. private void draw(Canvas canvas){
  11.  //该方法会做如下事情
  12.  //1 、绘制该View的背景
  13.  //2、为绘制渐变框做一些准备操作
  14.  //3、调用onDraw()方法绘制视图本身
  15.  //4、调用dispatchDraw()方法绘制每个子视图,dispatchDraw()已经在Android框架中实现了,在ViewGroup方法中。
  16.       // 应用程序程序一般不需要重写该方法,但可以捕获该方法的发生,做一些特别的事情。
  17.  //5、绘制渐变框  
  18. }
  19. //ViewGroup.java中的dispatchDraw()方法,应用程序一般不需要重写该方法
  20. @Override
  21. protected void dispatchDraw(Canvas canvas) {
  22.  // 
  23.  //其实现方法类似如下:
  24.  int childCount = getChildCount() ;
  25.  for(int i=0 ;i<childCount ;i++){
  26.   View child = getChildAt(i) ;
  27.   //调用drawChild完成
  28.   drawChild(child,canvas) ;
  29.  }
  30. }
  31. //ViewGroup.java中的dispatchDraw()方法,应用程序一般不需要重写该方法
  32. protected void drawChild(View child,Canvas canvas) {
  33.  // ….
  34.  //简单的回调View对象的draw()方法,递归就这么产生了。
  35.  child.draw(canvas) ;
  36.  //………
  37. }
   关于绘制背景图片详细的过程,请参考我的另外的博客:

           

              <<Android中View(视图)绘制不同状态背景图片原理深入分析以及StateListDrawable使用详解>>

 

    强调一点的就是,在这三个流程中,Google已经帮我们把draw()过程框架已经写好了,自定义的ViewGroup只需要实现

 measure()过程和layout()过程即可 。

 

     这三种情况,最终会直接或间接调用到三个函数,分别为invalidate(),requsetLaytout()以及requestFocus() ,接着

这三个函数最终会调用到ViewRoot中的schedulTraversale()方法,该函数然后发起一个异步消息,消息处理中调用

performTraverser()方法对整个View进行遍历。

 

 

    invalidate()方法 :

 

   说明:请求重绘View树,即draw()过程,假如视图发生大小没有变化就不会调用layout()过程,并且只绘制那些“需要重绘的”

视图,即谁(View的话,只绘制该View ;ViewGroup,则绘制整个ViewGroup)请求invalidate()方法,就绘制该视图。

 

     一般引起invalidate()操作的函数如下:

            1、直接调用invalidate()方法,请求重新draw(),但只会绘制调用者本身。

            2、setSelection()方法 :请求重新draw(),但只会绘制调用者本身。

            3、setVisibility()方法 : 当View可视状态在INVISIBLE转换VISIBLE时,会间接调用invalidate()方法,

                     继而绘制该View。

            4 、setEnabled()方法 : 请求重新draw(),但不会重新绘制任何视图包括该调用者本身。

 

    requestLayout()方法 :会导致调用measure()过程 和 layout()过程 。

 

           说明:只是对View树重新布局layout过程包括measure()和layout()过程,不会调用draw()过程,但不会重新绘制

任何视图包括该调用者本身。

 

    一般引起invalidate()操作的函数如下:

         1、setVisibility()方法:

             当View的可视状态在INVISIBLE/ VISIBLE 转换为GONE状态时,会间接调用requestLayout() 和invalidate方法。

    同时,由于整个个View树大小发生了变化,会请求measure()过程以及draw()过程,同样地,只绘制需要“重新绘制”的视图。

 

    requestFocus()函数说明:

 

          说明:请求View树的draw()过程,但只绘制“需要重绘”的视图。

 

 

    下面写个简单的小Demo吧,主要目的是给大家演示绘图的过程以及每个流程里该做的一些功能。截图如下:

 

                                                

 

 

 

1、    MyViewGroup.java  自定义ViewGroup类型

   

  1. /**
  2.  * @author http://http://blog.csdn.net/qinjuning
  3.  */
  4. //自定义ViewGroup 对象
  5. public class MyViewGroup  extends ViewGroup{
  6.     private static String TAG = “MyViewGroup” ;
  7.     private Context mContext ;
  8.     public MyViewGroup(Context context) {
  9.         super(context);
  10.         mContext = context ;
  11.         init() ;
  12.     }
  13.     //xml定义的属性,需要该构造函数
  14.     public MyViewGroup(Context context , AttributeSet attrs){
  15.         super(context,attrs) ;
  16.         mContext = context ;
  17.         init() ;
  18.     }
  19.     //为MyViewGroup添加三个子View
  20.     private void init(){
  21.         //调用ViewGroup父类addView()方法添加子View
  22.         //child 对象一 : Button
  23.         Button btn= new Button(mContext) ;
  24.         btn.setText(“I am Button”) ;
  25.         this.addView(btn) ;
  26.         //child 对象二 : ImageView 
  27.         ImageView img = new ImageView(mContext) ;
  28.         img.setBackgroundResource(R.drawable.icon) ;
  29.         this.addView(img) ;
  30.         //child 对象三 : TextView
  31.         TextView txt = new TextView(mContext) ;
  32.         txt.setText(“Only Text”) ;
  33.         this.addView(txt) ;
  34.         //child 对象四 : 自定义View
  35.         MyView myView = new MyView(mContext) ;
  36.         this.addView(myView) ;
  37.     }
  38.     @Override
  39.     //对每个子View进行measure():设置每子View的大小,即实际宽和高
  40.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
  41.         //通过init()方法,我们为该ViewGroup对象添加了三个视图 , Button、 ImageView、TextView
  42.         int childCount = getChildCount() ;
  43.         Log.i(TAG, “the size of this ViewGroup is —-> “ + childCount) ;
  44.         Log.i(TAG, “**** onMeasure start *****”) ;
  45.         //获取该ViewGroup的实际长和宽  涉及到MeasureSpec类的使用
  46.         int specSize_Widht = MeasureSpec.getSize(widthMeasureSpec) ;
  47.         int specSize_Heigth = MeasureSpec.getSize(heightMeasureSpec) ;
  48.         Log.i(TAG, “**** specSize_Widht “ + specSize_Widht+ ” * specSize_Heigth   *****” + specSize_Heigth) ;
  49.         //设置本ViewGroup的宽高
  50.         setMeasuredDimension(specSize_Widht , specSize_Heigth) ;
  51.         for(int i=0 ;i<childCount ; i++){
  52.             View child = getChildAt(i) ;   //获得每个对象的引用
  53.             child.measure(5050) ;   //简单的设置每个子View对象的宽高为 50px , 50px  
  54.             //或者可以调用ViewGroup父类方法measureChild()或者measureChildWithMargins()方法
  55.             //this.measureChild(child, widthMeasureSpec, heightMeasureSpec) ;
  56.         }
  57.     }
  58.     @Override
  59.     //对每个子View视图进行布局
  60.     protected void onLayout(boolean changed, int l, int t, int r, int b) {
  61.         // TODO Auto-generated method stub
  62.         //通过init()方法,我们为该ViewGroup对象添加了三个视图 , Button、 ImageView、TextView
  63.         int childCount = getChildCount() ;
  64.         int startLeft = 0 ;//设置每个子View的起始横坐标 
  65.         int startTop = 10 ; //每个子View距离父视图的位置 , 简单设置为10px吧 。 可以理解为 android:margin=10px ;
  66.         Log.i(TAG, “**** onLayout start ****”) ;
  67.         for(int i=0 ;i<childCount ; i++){
  68.             View child = getChildAt(i) ;   //获得每个对象的引用
  69.             child.layout(startLeft, startTop, startLeft+child.getMeasuredWidth(), startTop+child.getMeasuredHeight()) ;
  70.             startLeft =startLeft+child.getMeasuredWidth() + 10;  //校准startLeft值,View之间的间距设为10px ;
  71.             Log.i(TAG, “**** onLayout startLeft ****” +startLeft) ;
  72.         }
  73.     }
  74.     //绘图过程Android已经为我们封装好了 ,这儿只为了观察方法调用程
  75.     protected void dispatchDraw(Canvas canvas){
  76.         Log.i(TAG, “**** dispatchDraw start ****”) ;
  77.         super.dispatchDraw(canvas) ;
  78.     }
  79.     protected boolean drawChild(Canvas canvas , View child, long drawingTime){
  80.         Log.i(TAG, “**** drawChild start ****”) ;
  81.         return super.drawChild(canvas, child, drawingTime) ;
  82.     }
  83. }

   

          2、MyView.java 自定义View类型,重写onDraw()方法 ,

 

  1. //自定义View对象
  2.     public class MyView extends View{
  3.         private Paint paint  = new Paint() ;
  4.         public MyView(Context context) {
  5.             super(context);
  6.             // TODO Auto-generated constructor stub
  7.         }
  8.         public MyView(Context context , AttributeSet attrs){
  9.             super(context,attrs);
  10.         }
  11.         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
  12.             //设置该View大小为 80 80
  13.             setMeasuredDimension(50 , 50) ;
  14.         }
  15.         //存在canvas对象,即存在默认的显示区域
  16.         @Override
  17.         public void onDraw(Canvas canvas) {
  18.             // TODO Auto-generated method stub
  19.             super.onDraw(canvas);
  20.             Log.i(“MyViewGroup”“MyView is onDraw “) ;
  21.             //加粗
  22.             paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
  23.             paint.setColor(Color.RED);
  24.             canvas.drawColor(Color.BLUE) ;
  25.             canvas.drawRect(003030, paint);
  26.             canvas.drawText(“MyView”1040, paint);
  27.         }
  28.     }

 

 

  主Activity只是显示了该xml文件,在此也不罗嗦了。 大家可以查看该ViewGroup的Log仔细分析下View的绘制流程以及

相关方法的使用。第一次启动后捕获的Log如下,网上找了些资料,第一次View树绘制过程会走几遍,具体原因可能是某些

View 发生了改变,请求重新绘制,但这根本不影响我们的界面显示效果 。

 

        总的来说: 整个绘制过程还是十分十分复杂地,每个具体方法的实现都是我辈难以立即的,感到悲剧啊。对Android提

 供的一些ViewGroup对象,比如LinearLayout、RelativeLayout布局对象的实现也很有压力。 本文重在介绍整个View树的绘制

流程,希望大家在此基础上,多接触源代码进行更深入地扩展。

      

       示例DEMO下载地址:http://download.csdn.net/detail/qinjuning/3982468

Android中visibility属性VISIBLE、INVISIBLE、GONE的区别

Android开发中,大部分控件都有visibility这个属性,其属性有3个分别为“visible ”、“invisible”、“gone”。主要用来设置控制控件的显示和隐藏。有些人可能会疑惑Invisible和gone是有什么区别的???那么,我们带着这个疑问看下面:

其在XML文件和Java代码中设置如下:

可见(visible)

XML文件:android:visibility=”visible”

Java代码:view.setVisibility(View.VISIBLE);

 

不可见(invisible)

XML文件:android:visibility=”invisible”

Java代码:view.setVisibility(View.INVISIBLE);

 

隐藏(GONE)

XML文件:android:visibility=”gone”

Java代码:view.setVisibility(View.GONE);

 

 

为了区别三者,我建了一个Dome进行演示,先上Dome的代码,演示后就知道它们的区别:

XML文件:

  1. <?xml version=“1.0” encoding=“utf-8”?>
  2. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”
  3.     android:layout_width=“fill_parent”
  4.     android:layout_height=“fill_parent”
  5.     android:orientation=“vertical”>
  6.     <LinearLayout
  7.         android:layout_width=“fill_parent”
  8.         android:layout_height=“wrap_content”
  9.         android:orientation=“horizontal”
  10.         android:layout_marginBottom=“20dip” >
  11.         <TextView
  12.             android:layout_width=“wrap_content”
  13.             android:layout_height=“wrap_content”
  14.             android:layout_weight=“1”
  15.             android:background=“#F00”
  16.             android:text=“TextView1”
  17.             android:textSize=“23sp”
  18.             android:visibility=“visible” />
  19.         <TextView
  20.             android:id=“@+id/mainTV2”
  21.             android:layout_width=“wrap_content”
  22.             android:layout_height=“wrap_content”
  23.             android:layout_weight=“1”
  24.             android:background=“#00F”
  25.             android:text=“TextView2”
  26.             android:textSize=“23sp”
  27.             android:visibility=“visible” />
  28.     </LinearLayout>
  29.     <Button
  30.         android:id=“@+id/mainBtn1”
  31.         android:layout_width=“fill_parent”
  32.         android:layout_height=“wrap_content”
  33.         android:text=“TextView2为VISIBLE”
  34.         android:onClick=“mianOnClickListener”/>
  35.     <Button
  36.         android:id=“@+id/mainBtn2”
  37.         android:layout_width=“fill_parent”
  38.         android:layout_height=“wrap_content”
  39.         android:text=“TextView2为INVISIBLE”
  40.         android:onClick=“mianOnClickListener”/>
  41.     <Button
  42.         android:id=“@+id/mainBtn3”
  43.         android:layout_width=“fill_parent”
  44.         android:layout_height=“wrap_content”
  45.         android:text=“TextView2为GONE”
  46.         android:onClick=“mianOnClickListener”/>
  47. </LinearLayout>

后面三个Button只要是控制TextView的visibility的属性

Java代码:

  1. package com.chindroid.visibility;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.view.View;
  5. import android.widget.TextView;
  6. public class MainActivity extends Activity {
  7.     /** TextView2 */
  8.     private TextView mainTV2 = null;
  9.     @Override
  10.     public void onCreate(Bundle savedInstanceState) {
  11.         super.onCreate(savedInstanceState);
  12.         setContentView(R.layout.main);
  13.         //初始化数据
  14.         initData();
  15.     }
  16.     /** 初始化控件的方法 */
  17.     private void initData() {
  18.         mainTV2 = (TextView)findViewById(R.id.mainTV2);
  19.     }
  20.     /**
  21.      * MainActivity中响应按钮点击事件的方法
  22.      * 
  23.      * @param v
  24.      */
  25.     public void mianOnClickListener(View v){
  26.         switch (v.getId()){
  27.             case R.id.mainBtn1:{    //按钮1的响应事件
  28.                 //设置TextView2可见
  29.                 mainTV2.setVisibility(View.VISIBLE);
  30.                 break;
  31.             }
  32.             case R.id.mainBtn2:{    //按钮2的响应事件
  33.                 //设置TextView2不可见
  34.                 mainTV2.setVisibility(View.INVISIBLE);
  35.                 break;
  36.             }
  37.             case R.id.mainBtn3:{    //按钮3的响应事件
  38.                 //设置TextView2隐藏
  39.                 mainTV2.setVisibility(View.GONE);
  40.                 break;
  41.             }
  42.             default:
  43.                 break;
  44.         }
  45.     }
  46. }

由于程序一启动两个TextView都是可见的

当我们点击第1个按钮,把TextView2 visibility属性设置为INVISIBLE时,程序如下如下图所示:

当我们点击第3个按钮,把TextView2 visibility属性设置为GONE时,程序如下如下图所示:

当我们再点击第1个按钮,把TextView2 visibility属性设置为VISIBLE时,TextView2又呈现出来了,如下图所示:

 

由上面的演示可知

VISIBLE:设置控件可见

INVISIBLE:设置控件不可见

GONE:设置控件隐藏

 

而INVISIBLE和GONE的主要区别是:当控件visibility属性为INVISIBLE时,界面保留了view控件所占有的空间;而控件属性为GONE时,界面则不保留view控件所占有的空间。