Android 高级图形图像处理

这篇博客详细介绍了在Android中实现高级图形图像处理的各种技术,包括绘制各种形状、渐变效果、图像裁剪、动画效果等。通过示例代码展示了如何使用shape文件和XML布局实现线性、扫描、放射状梯度渐变,以及如何运用属性动画实现透明、旋转、位移等效果。此外,还涵盖了拖曳管控、横竖屏切换视图适配、自定义View的尺寸和位置控制,以及状态栏消息通知的实现。

1. 绘制矩形、扇形、圆角矩形

// activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"

    >
    <TextView
        android:layout_width="150dp"
        android:layout_height="100dp"
        android:id="@+id/textview1" />
    <TextView
        android:layout_width="150dp"
        android:layout_height="100dp"
        android:id="@+id/textview2"/>
    <TextView
        android:layout_width="150dp"
        android:layout_height="100dp"
        android:id="@+id/textview3"/>
    <TextView
        android:layout_width="150dp"
        android:layout_height="100dp"
        android:id="@+id/textview4"/>

</LinearLayout>
//main.java
 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //圆角矩形
        //PaintDrawable继承自ShapeDrawable
        PaintDrawable drawable1=new PaintDrawable(Color.GREEN);
        drawable1.setCornerRadius(60);//圆角弧度
        findViewById(R.id.textview1).setBackgroundDrawable(drawable1);

        //椭圆
        OvalShape ovalShape = new OvalShape();
        ShapeDrawable drawable2=new ShapeDrawable(ovalShape);
        drawable2.getPaint().setColor(Color.BLUE);
        drawable2.getPaint().setStyle(Paint.Style.FILL);//填充形式为完全填充
        findViewById(R.id.textview2).setBackgroundDrawable(drawable2);

        //矩形
        RectShape rectShape =new RectShape();
        ShapeDrawable drawable3=new ShapeDrawable(rectShape);
        drawable3.getPaint().setColor(Color.RED);
        drawable3.getPaint().setStyle(Paint.Style.FILL);//填充形式为完全填充
        findViewById(R.id.textview3).setBackgroundDrawable(drawable3);

        //弧度扇形
        ArcShape arcShape = new ArcShape(100,120);//顺时针开始角度100°, 绘制区域为120°
        ShapeDrawable drawable4=new ShapeDrawable(arcShape);
        drawable4.getPaint().setColor(Color.BLACK);
        drawable4.getPaint().setStyle(Paint.Style.FILL);//填充形式为完全填充
        findViewById(R.id.textview4).setBackgroundDrawable(drawable4);

    }

2.用xml代码布局文件实现复杂的图像

IDE: Android studio
【1】创建Shape文件:
	(1)	右击app->New->Android resource file
	(2)	File name: 填写Shape文件的名字(如 gradient)
	(3)	Resource type: Drawable
	(4)	Root element: shape
	(5)	Shape文件中的代码如下:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval"> // 定义一个椭圆

    <gradient
        android:angle="45"	
        android:centerColor="@android:color/holo_orange_dark"
        android:endColor="@android:color/holo_blue_dark"
        android:startColor="@android:color/holo_red_dark" />

    <stroke	//描边
        android:width="10dip"		//所描边的厚度
        android:color="@android:color/black"		
        android:dashGap="50dip"	//生成不连续间断的长度
        android:dashWidth="111dip"		//黑色断片的长度
        />

</shape>

【2】在布局文件activity_main.xml中加入
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    >

    <ImageView
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:id="@+id/image_view"
        android:layout_centerInParent="true"
        android:background="@drawable/gradient"
        />

</RelativeLayout>

【3】java代码中只需
setContentView(R.layout.activity_main);


3.圆环形渐变

shape文件代码如下(替换实例2的shap文件)

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="ring"
    android:thickness="20dp"
    android:useLevel="false">

    <gradient
        android:angle="90"
        android:centerColor="@android:color/holo_orange_dark"
        android:endColor="@android:color/holo_blue_dark"
        android:startColor="@android:color/holo_red_dark"/>

    <stroke
        android:width="5dp"
        android:color="@android:color/black"/>


</shape>

4.矩形渐变

shape文件如下(替换实例2的shap文件)

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <gradient
        android:angle="90"
        android:centerColor="@android:color/holo_orange_dark"
        android:endColor="@android:color/holo_blue_dark"
        android:startColor="@android:color/holo_red_dark"/>

    <stroke
        android:width="5dp"
        android:color="@android:color/black"/>

</shape>

5.渐变线

shape文件如下(替换实例2的shap文件)

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <gradient
        android:angle="0"
        android:centerColor="@android:color/holo_blue_light"
        android:endColor="@android:color/transparent"     //transparent为黑色全透明
        android:startColor="@android:color/transparent"/>

    <size android:height="1px"/>

</shape>

6.动态渐变

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Drawable[] drawables=new Drawable[]{new ColorDrawable(Color.TRANSPARENT),new ColorDrawable(Color.RED)};

        TransitionDrawable mTransitionDrawable=new TransitionDrawable(drawables);

        ImageView image=(ImageView)findViewById(R.id.image_view);
        image.setImageDrawable(mTransitionDrawable);

        mTransitionDrawable.setCrossFadeEnabled(true);
        mTransitionDrawable.startTransition(10000);
        
    }

7.对图片进行裁剪

#正常图像
        ImageView image1=(ImageView) findViewById(R.id.image_view1);
        image1.setImageResource(R.drawable.yes);

#矩形四角磨成圆脚
        RoundedBitmapDrawable round= RoundedBitmapDrawableFactory.create(getResources()
        , BitmapFactory.decodeResource(getResources(),R.drawable.yes));
        round.getPaint().setAntiAlias(true);
        round.setCornerRadius(30);
        ImageView image2=(ImageView) findViewById(R.id.image_view2);
        image2.setImageDrawable(round);

#裁剪为圆形图像
        Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.yes);
        RoundedBitmapDrawable circle= RoundedBitmapDrawableFactory.create(getResources()
                , BitmapFactory.decodeResource(getResources(),R.drawable.yes));
        circle.getPaint().setAntiAlias(true);
        circle.setCornerRadius(Math.max(bitmap.getWidth(),bitmap.getHeight()));
        ImageView image3=(ImageView) findViewById(R.id.image_view3);
        image3.setImageDrawable(circle);

8.灵活的调用图片在Imageview显示

p.xml(在drawable中)代码如下:

<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:maxLevel="1" android:drawable="@drawable/a"/>
    <item android:maxLevel="2" android:drawable="@drawable/b"/>
    <item android:maxLevel="3" android:drawable="@drawable/c"/>

</level-list>

*.java

        ImageView image1=(ImageView) findViewById(R.id.image_view1);
        image1.setImageResource(R.drawable.p);
        image1.setImageLevel(3);

9.红色小圆球样式的新消息提醒

*.java

setContentView(R.layout.activity_main);

shape文件(round.xml)

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <!--填充颜色值-->
    <solid android:color="#FFA500"/>
    <!--radius值越大,越趋于圆形-->
    <corners android:radius="5dip" />
    <!--圆角图像内部填充四周的大小,将会已此挤压内部布置的view-->
    <padding
        android:bottom="3dip"
        android:left="3dip"
        android:right="3dip"
        android:top="3dip"/>
</shape>

shape文件(tips_circle.xml)

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval"
    android:useLevel="false">
    <solid android:color="#FF0000"/>

</shape>

布局文件

<?xml version="1.0" encoding="utf-8"?>
<!--(帧布局)
android:layout_margin就是设置view的上下左右边框的额外空间-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="1dp">

    <!--用mipmap系统会在缩放上提供一定的性能优化。
        src是图片内容(前景),background是背景,可以同时使用-->
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/image_view"
        android:layout_margin="10dip"
        android:src="@mipmap/ic_launcher"
        android:background="@drawable/round" />

    <!--layout_gravity是相对于父布局来说.gravity就是相对于控件本身来用的.
    layout_gravity是想在父布局里面改变位置的时候用的,
    而gravity是想改变自己内部东西位置是用的.
    android:textStyle设置字形[bold(粗体) 0, italic(斜体) 1, bolditalic(又粗又斜) 2]
    可以设置一个或多个,用“|”隔开-->
    <TextView
        android:layout_width="25dip"
        android:layout_height="25dip"
        android:layout_gravity="right|top"
        android:background="@drawable/tips_circle"
        android:gravity="center"
        android:text="5"
        android:textColor="@android:color/white"
        android:textSize="15dip"
        android:textStyle="bold"/>

</FrameLayout>

Android常见的渲染器有三种:LinearGradient、SweepGradient、RadialGradient

10.线性梯度渐变渲染(LinearGradient)

//activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:id="@+id/li"> 

</LinearLayout>

重写view中的ondraw,LinearGradientVIew .java

public class LinearGradientVIew extends View {
//
    LinearGradient linearGrandient1,linearGrandient2,linearGrandient3,linearGrandient4,linearGrandient5;
    Paint paint1,paint2,paint3,paint4,paint5;

    public LinearGradientVIew(Context context) {
        super(context);
        linearGrandient1=new LinearGradient(0,0,300,0,new int[]{Color.RED,Color.YELLOW,Color.BLUE},null, Shader.TileMode.REPEAT);
        paint1=new Paint();

        linearGrandient2=new LinearGradient(0,0,300,0,new int[]{Color.RED,Color.YELLOW,Color.BLUE},null, Shader.TileMode.MIRROR);
        paint2=new Paint();

        linearGrandient3=new LinearGradient(0,0,300,0,new int[]{Color.RED,Color.YELLOW,Color.BLUE},null, Shader.TileMode.CLAMP);
        paint3=new Paint();

        //绘制起点(0,0),绘制终点(100,100)。然后已此图形进行对称渲染,渲染样式为CLAMP(拉伸模式)、MIRROR(平面镜)、REPEAT(重复渲染)
        linearGrandient4=new LinearGradient(0,0,100,100,new int[]{Color.RED,Color.YELLOW,Color.BLUE},null, Shader.TileMode.REPEAT);
        paint4=new Paint();

        linearGrandient5=new LinearGradient(0,300,300,0,new int[]{Color.RED,Color.YELLOW,Color.BLUE},null, Shader.TileMode.REPEAT);
        paint5=new Paint();

    }

    //onDraw()方法负责绘制,即如果我们希望得到的效果在Android原生控件中没有现成的支持,
    // 那么我们就需要自己绘制我们的自定义控件的显示效果。
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int left=10,top=20,right=0,bottom=0;
        right=this.getWidth()-left;

        bottom=this.getHeight()/5;

        paint1.setShader(linearGrandient1);
        Rect rect1=new Rect(left,top,right,bottom);
        canvas.drawRect(rect1,paint1);

        top=top+bottom;

        paint2.setShader(linearGrandient2);
        Rect rect2=new Rect(left,top,right,bottom*2);
        canvas.drawRect(rect2,paint2);

        top=top+bottom;

        paint3.setShader(linearGrandient3);
        Rect rect3=new Rect(left,top,right,bottom*3);
        canvas.drawRect(rect3,paint3);

        top=top+bottom;

        paint4.setShader(linearGrandient4);
        Rect rect4=new Rect(left,top,right,bottom*4);
        canvas.drawRect(rect4,paint4);

        top=top+bottom;

        paint5.setShader(linearGrandient5);
        Rect rect5=new Rect(left,top,right,bottom*5);
        canvas.drawRect(rect5,paint5);
    }

}

main_activity.java

public class MainActivity extends Activity {

    LinearGradientVIew view1;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            view1=new LinearGradientVIew(getApplicationContext());
            LinearLayout lin=(LinearLayout)findViewById(R.id.li);
            lin.addView(view1);

        }
}

11.扫描梯度渐变渲染器(SweepGradient)

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:id="@+id/li">
    
	<!--包名.类名-->
    <yu.myapplication.SweepGradientView
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"></yu.myapplication.SweepGradientView>

</LinearLayout>

SweepGradientView .java

public class SweepGradientView extends View {
    SweepGradient mSweepGradient;
    Paint mPaint;
    Canvas canvas;

    ////在MainActivity.java文件中调用自己写的view时调用该构造方法
    public SweepGradientView(Context context) {
        super(context);
    }

    //在xml布局文件中调用自己写的view时调用改构造方法
    public SweepGradientView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

        @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        mSweepGradient=new SweepGradient(this.getWidth()/2,this.getHeight()/2,
                new int[]{Color.TRANSPARENT,Color.RED,Color.TRANSPARENT,Color.
                        YELLOW,Color.BLUE},null);

        mPaint=new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setShader(mSweepGradient);
        canvas.drawCircle(this.getWidth()/2,this.getHeight()/2,300,mPaint);
    }

    //Android通过onMeasure()测量View的大小,默认情况下是EXACTLY模式
    //EXACTLY 精确模式 例如layout_height=”50dp”或是=”match_parent”
    //AT_MOST 最大值模式 就是warp_content
    //UNSPECIFIED 通过字面意思就是没有指定尺寸
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //先声明两个int值来表示最终的width和height并给定一个默认的大小
        int width_size  = 100;
        int height_size = 100;

        //使用MeasureSpec分别获取width和height的MODE和SIZE
        int HEIGHT_MODE = MeasureSpec.getMode(heightMeasureSpec);
        int HEIGHT_SIZE = MeasureSpec.getSize(heightMeasureSpec);
        int WIDTH_MODE = MeasureSpec.getMode(widthMeasureSpec);
        int WIDTH_SIZE = MeasureSpec.getSize(widthMeasureSpec);
        if (HEIGHT_MODE == MeasureSpec.EXACTLY) {
            height_size = HEIGHT_SIZE;       //如果测量模式是精确的话 那么就直接使用获取到的值就好了
        }else if (HEIGHT_MODE == MeasureSpec.AT_MOST){  //如果是最大值模式的话 那么久比较获取的和设定的默认值那个小就使用那个.ps:Math.min(int a,int b)是比较数值大小的.
            height_size = Math.min(HEIGHT_SIZE,height_size);
        }
        if (WIDTH_MODE == MeasureSpec.EXACTLY){
            width_size = WIDTH_SIZE;
        }else if (WIDTH_MODE == MeasureSpec.AT_MOST){
            width_size = Math.min(WIDTH_SIZE,width_size);
        }

        //测量完毕后得到的了width和height通过setMeasuredDimension()设置width和height,真正决定具体大小的是setMeasuredDimension()的两个参数.
        setMeasuredDimension(width_size,height_size);
    }
}

MainActivity.java

public class MainActivity extends Activity {

    SweepGradientView view1;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
}

12.放射环状梯度渐变渲染器(RadialGradient)

RadialGradientView .java

public class RadialGradientView extends View {

    private float radius =480;
    private RadialGradient mRadialGradient;
    private Paint mPaint;

    public RadialGradientView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mRadialGradient=new RadialGradient(this.getWidth()/2,this.getHeight()/2,
                radius,new int[]{Color.RED,Color.TRANSPARENT,Color.BLACK},null,
                Shader.TileMode.CLAMP);
                
        mPaint=new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setShader(mRadialGradient);
        canvas.drawCircle(this.getWidth()/2,this.getHeight()/2,radius,mPaint);
    }
}

MainActivity.java与例子11相同。只需将例11中activity_main.xml里的包名.类名改为包名.RadialGradientView 。


13.用shape文件实现线性梯度渐变渲染

gradient.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <!--angle(渐变的角度)设置为0或者180,决定渐变的方向是水平地从左往右,还是从右往左.-->
    <gradient
        android:angle="45"
        android:centerColor="@android:color/holo_orange_light"
        android:endColor="@android:color/holo_blue_light"
        android:startColor="@android:color/holo_red_light"
        android:type="linear"/>
</shape>

MainActivity.java和activity_main.xml与例子2中的一样


14.用shape文件实现放射状梯度渐变渲染

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <!--gradientRadius为渐变尺度(半径),centerX和centerY为渐变圆的圆心取值为0~1的浮点数-->
    <gradient
        android:centerColor="@android:color/holo_red_light"
        android:endColor="@android:color/holo_blue_light"
        android:startColor="@android:color/holo_orange_light"
        android:gradientRadius="200dp"
        android:centerX="0"
        android:centerY="0.8"
        android:type="radial" />
</shape>

MainActivity.java和activity_main.xml与例子2中的一样


15.用shape文件实现扫描形梯度渐变渲染

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <!--centerX和centerY为渐变中心取值为0~1的浮点数-->
    <gradient
        android:centerColor="@android:color/holo_red_light"
        android:endColor="@android:color/holo_blue_light"
        android:startColor="@android:color/holo_orange_light"
        android:centerX="0.5"
        android:centerY="0.5"
        android:type="sweep" />
</shape>

MainActivity.java和activity_main.xml与例子2中的一样


16.透明渐变属性动画

public class MainActivity extends Activity {

    TextView mTextView;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mTextView= (TextView) findViewById(R.id.li);
            startPropertyAnim();//动画的执行要在主线程中执行
        }

    private void startPropertyAnim(){
        //1->0.1->1->0.5->1(完全不透明)
        ObjectAnimator mObjectAnimator = ObjectAnimator.ofFloat(mTextView,"alpha",1f,0.1f,1f,0.5f,1f);

        mObjectAnimator.setDuration(5000);//动画持续时间

        //这是一个回掉监听,获取属性动画在执行期间的具体值
        /*mObjectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value= (float) animation.getAnimatedValue();
                //Log.d("动画的值",value+"");

            }
        });*/
        mObjectAnimator.start();
    }
}

17.旋转属性动画

public class MainActivity extends Activity {

    TextView mTextView;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mTextView= (TextView) findViewById(R.id.li);
            startPropertyAnim();//动画的执行要在主线程中执行
        }

    private void startPropertyAnim(){
        //rotation(旋转动画),0f~360f为mTextView顺时针旋转360度
        ObjectAnimator mObjectAnimator = ObjectAnimator.ofFloat(mTextView,"rotation",0f,360f);

        mObjectAnimator.setDuration(5000);//动画持续时间

        //这是一个回掉监听,获取属性动画在执行期间的具体值
        /*mObjectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value= (float) animation.getAnimatedValue();
                //Log.d("动画的值",value+"");

            }
        });*/
        mObjectAnimator.start();
    }
}

18.位移属性动画

public class MainActivity extends Activity implements View.OnClickListener{

    TextView mTextView;
    ObjectAnimator mObjectAnimator;
    Button but;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mTextView= (TextView) findViewById(R.id.li);
            startPropertyAnim();//动画的执行要在主线程中执行

            but= (Button) findViewById(R.id.button);
            but.setOnClickListener(this);
        }

    private void startPropertyAnim(){
        float translationX=mTextView.getTranslationX();
        //translationX(水平位移)、translationY(垂直位移);
        //ofFloat(,,开始位置,中间位置,结束位置)
        mObjectAnimator = ObjectAnimator.ofFloat(mTextView,"translationX",translationX,500f,translationX);

        mObjectAnimator.setDuration(5000);//动画持续时间
        
        //mObjectAnimator.start();
    }

    @Override
    public void onClick(View v) {
        mObjectAnimator.start();
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:background="@android:color/black">

    <TextView
        android:layout_width="48dp"
        android:layout_height="140dp"
        android:layout_marginTop="20dp"
        android:layout_marginLeft="150dp"
        android:text="YE"
        android:background="@android:color/holo_blue_light"
        android:id="@+id/li"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/button"
        android:layout_margin="90dp"
        android:text="yes"
        android:layout_gravity="center_horizontal" />

</LinearLayout>

19.拉申属性动画

替换例18中的startPropertyAnim就可。

    private void startPropertyAnim(){

        //ofFloat(,,开始拉申长度,中间拉申长度,结束拉申长度)
        mObjectAnimator = ObjectAnimator.ofFloat(mTextView,"scaleX",1f,5f,5f);

        mObjectAnimator.setDuration(5000);//动画持续时间

        //mObjectAnimator.start();
    }

20.酷炫的动画往往不止一个效果,而是同时,或是按照一定序列联合执行一组动画集。AnimatorSet(动画集)

public class MainActivity extends Activity implements View.OnClickListener{

    TextView mTextView;
    ObjectAnimator alpha,rotation,translationX,scaley;
    Button but;
    AnimatorSet mAnimatorset;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mTextView= (TextView) findViewById(R.id.li);
            startPropertyAnim();//动画的执行要在主线程中执行

            but= (Button) findViewById(R.id.button);
            but.setOnClickListener(this);
        }

    private void startPropertyAnim(){
        //透明渐变
        alpha = ObjectAnimator.ofFloat(mTextView,"alpha",1f,0f,1f);
        //旋转
        rotation= ObjectAnimator.ofFloat(mTextView,"rotation",0f,360f);
        //位移
        float translationx=mTextView.getTranslationX();
        translationX=ObjectAnimator.ofFloat(mTextView,"translationX",translationx,200f,translationx);
        //缩放
        scaley= ObjectAnimator.ofFloat(mTextView,"scaleY",1f,5f,1f);

        mAnimatorset=new AnimatorSet();
        mAnimatorset.setDuration(1000);//动画持续时间

        mAnimatorset.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                //动画结束
            }

            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                //动画开始
            }
        });
        /*
        play()
        with(a) a与现有动画同时执行
        before(a) a在现有动画之后执行
        after(a) a在现有动画之前执行
         */
        //先执行after(),再次同时执行play()和with(),其次执行before()
        mAnimatorset.play(alpha).with(rotation).before(scaley).after(translationX);
    }

    @Override
    public void onClick(View v) {
        mAnimatorset.start();
    }
}

布局文件在例18


21.拖曳管控

1.实现三个TextView的拖曳
// myActivity.java

public class myActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my_layout);
    }
}

// MyLayout.java

public class MyLayout extends LinearLayout {

    private ViewDragHelper mViewDragHelper;

    public MyLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        mViewDragHelper=ViewDragHelper.create(this,1.0f,new ViewDragHelperCallback());
    }

    //回调函数
    private class ViewDragHelperCallback extends ViewDragHelper.Callback{

        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            ///return false;
            return true; //view允许被托曳移动
        }

        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            //return super.clampViewPositionHorizontal(child, left, dx);
            return left;
        }

        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            //return super.clampViewPositionVertical(child, top, dy);
            return top;
        }

        @Override
        public void onViewDragStateChanged(int state) {
            super.onViewDragStateChanged(state);
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //return super.onInterceptTouchEvent(ev);
        return mViewDragHelper.shouldInterceptTouchEvent(ev);
    }

    //分析处有效的屏幕滑动事件
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //return super.onTouchEvent(event);
        mViewDragHelper.processTouchEvent(event);
        return true;
    }
}

// activity_my_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<yu.myapplication.MyLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/red"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@android:color/holo_red_light"/>

    <TextView
        android:id="@+id/yellow"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@android:color/holo_orange_light"/>

    <TextView
        android:id="@+id/blue"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@android:color/holo_blue_light"/>
</yu.myapplication.MyLayout>

2.实现三个TextView的拖曳让其不能越界

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my_layout);
    }
public class MyLayout extends LinearLayout {

    private ViewDragHelper mViewDragHelper;

    public MyLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        mViewDragHelper=ViewDragHelper.create(this,1.0f,new ViewDragHelperCallback());
    }

    //回调函数
    private class ViewDragHelperCallback extends ViewDragHelper.Callback{

        @Override
        public boolean tryCaptureView(View child, int pointerId) {

            //红色view不允许被托曳移动
            if(child.getId()==R.id.red)
                return false;

            return true; //view允许被托曳移动
        }

        //控制水平方向的view位置.left为该view左边水平坐标.
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            //return super.clampViewPositionHorizontal(child, left, dx);

            //getPaddingLeft()为距父view的左边宽度(是一个固定值,由父view设定.可认为子view的左起点宽度)
            if(getPaddingLeft()>left) //让子view不能往左边越界
                return getPaddingLeft();

            //getWidth()为父view的宽度
            //getPaddingLeft()为距父view的右边宽度(是一个固定值,由父view设定.可认为子view的右起点宽度)
            if(getWidth()-child.getWidth()-getPaddingRight()<left)//让子view不能往右边越界
                return getWidth()-child.getWidth()-getPaddingRight();
            return left;
        }

        //控制垂直方向的view位置,top为左上角纵坐标
        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            //return super.clampViewPositionVertical(child, top, dy);
            if(getHeight()-child.getHeight()-getPaddingBottom()<top)//让子view不能往下边越界
                return getHeight()-child.getHeight()-getPaddingBottom();

            if(top<getPaddingTop())//让子view不能往上边越界
                return getPaddingTop();
            return top;
        }

        @Override
        public void onViewDragStateChanged(int state) {
            super.onViewDragStateChanged(state);
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //return super.onInterceptTouchEvent(ev);
        return mViewDragHelper.shouldInterceptTouchEvent(ev);
    }

    //分析处有效的屏幕滑动事件
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //return super.onTouchEvent(event);
        mViewDragHelper.processTouchEvent(event);
        return true;
    }
}
<?xml version="1.0" encoding="utf-8"?>
<yu.myapplication.MyLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="60dp"
    android:paddingRight="30dp"
    android:orientation="vertical">
    <!--paddingLeft的作用是:让控件里的内容距控件边上有一定的距离,
    此时子view往左边60dp拉父view会将它覆盖.其它同理-->

    <TextView
        android:id="@+id/red"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@android:color/holo_red_light"/>

    <TextView
        android:id="@+id/yellow"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@android:color/holo_orange_light"/>

    <TextView
        android:id="@+id/blue"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@android:color/holo_blue_light"/>
</yu.myapplication.MyLayout>


22.横竖屏切换不同尺寸的view

(本节采用的是两套不同屏幕方向的xml布局文件资源,可灵活配置和快捷开发。降低后期对java代码的维护。)
Android layout属性大全:

https://www.jianshu.com/p/62237e236d0b

如何在Android Studio创建layout_land.xml:

https://blog.csdn.net/lin_dianwei/article/details/64123800

创建后:
在res/layout/activity_main.xml(2)下有两个布局文件分别是activity_main.xml(land)
和activity_main.xml(port)。land(横屏、大屏),port(竖屏、小屏).
注意:以下两个布局文件名都为activity_main.xml。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!--
        layout_centerInParent:相对于父元素完全居中
        gravity是设置自身内部元素的对齐方式。
    -->
    <TextView
        android:layout_width="200dp"
        android:layout_height="100dp"
        android:layout_centerInParent="true"
        android:gravity="center"
        android:background="@android:color/holo_red_light"
        android:text="竖屏布局"
        android:id="@+id/textView" />

</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!--
        layout_centerInParent:相对于父元素完全居中
        gravity是设置自身内部元素的对齐方式。
    -->
    <TextView
        android:layout_width="200dp"
        android:layout_height="100dp"
        android:layout_centerInParent="true"
        android:gravity="center"
        android:background="@android:color/holo_red_light"
        android:text="横屏布局"/>

</RelativeLayout>

*.java 的Activity中只需加

setContentView(R.layout.activity_main);

23.onMeasure控制view的尺寸大小

onMeasure才是从根本上决定View的尺寸大小的因素,它是view的一个函数。
MyView.java

public class MyView extends View {

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.setBackgroundColor(Color.RED);
    }

    //onMeasure可分为两个阶段
    //第一阶段:测量阶段
    //第二阶段:赋值阶段
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //测量宽度
        int width=measureWidth(100,widthMeasureSpec);
        //android提供个一些便捷的方式直接计算宽、高值。其实getDefaultSiz()和measureWidth()相似.
        int height = this.getDefaultSize(100,heightMeasureSpec);

        //给自定义view设置具体的宽和高
        setMeasuredDimension(width,height);
    }

    private int measureWidth(int size,int measureSpec){
        //size为初始化值
        int result = size;
        //获取测量模式
        int specMode = MeasureSpec.getMode(measureSpec);
        //该值为Android系统为view测量后默认的宽的值
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode){
            //没有明确定义,不常用
            case MeasureSpec.UNSPECIFIED:
                break;

            //最多,尽可能多,此种模式指父布局为wrap_content
            case MeasureSpec.AT_MOST:
                result=specSize;
                break;

            //精确的完全的,此种模式指父布局为match_content或一个具体值
            case MeasureSpec.EXACTLY:
                result = size;
                break;

        }
        return result;
    }
}

布局文件layout_1.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <yu.myprocject.MyView
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        ></yu.myprocject.MyView>

</LinearLayout>

Activity只需

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_1);
    }

}

24.onLayout控制子View的空间位置

onMeasure()控制View的具体宽、高大小。onLayout()控制View的空间摆放位置。
Android常用的布局,它们的最关键的工作就是摆放View的位置。这一过程的关键既是onLayout()。onLayout在onMeasure之后调用。

public class MyLayout extends ViewGroup {


    public MyLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    //onMeasure主要的作用就是测量ViewGroup的大小(即测量内部子View适合的大小,由此确定ViewGroup(容器)的大小)。
    /*  int widthMeasureSpec, int heightMeasureSpec 。他们是父类传递过来给当前view的一个建议值,
        虽然表面上看起来他们是int类型的数字,其实他们是由mode+size两部分组成的。
        前两位代表mode(测量模式),后面28位才是他们的实际数值(size)。*/
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int measuredWidth = 0;
        int measuredHeight= 0;

        //返回父View下直接子View的个数
        int count=getChildCount();

        for(int i=0;i<count;i++){
            View v= this.getChildAt(i);
            /*getVisibility()返回的值:
                    visible  0,显示控件,控件可点击
                    invisible 4,不显示控件,但保留控件所占有的空间,控件不可点击
                    gone 8,隐藏控件,即不保留控件所占有的空间,控件不可点击*/
            if(v.getVisibility()!=View.GONE){
                this.measureChild(v,widthMeasureSpec,heightMeasureSpec);//测量子View
                //getMeasuredWidth()获取的是View原始的大小,也就是这个View在XML文件中配置或者是代码中设置的宽度大小。
                measuredWidth=Math.max(measuredWidth,v.getMeasuredWidth());//取得最宽的子View作为宽度
                //结束循环后为所有子View高度之和
                measuredHeight+=v.getMeasuredHeight();

            }

        }

        measuredWidth+=getPaddingLeft()+getPaddingRight();
        measuredHeight+=getPaddingTop()+getPaddingBottom();
        this.setMeasuredDimension(measuredWidth,measuredHeight);
    }
    
    /* ViewGroup可认为是一个矩形容器,
       l可认为是该ViewGroup左边距屏幕左边框的间距;
       t可认为是该ViewGroup上边距屏幕上边框的间距;
       r可认为是该ViewGroup右边距屏幕左边框的距离;
       b可认为是该ViewGroup下边距屏幕上边框的距离;*/
    //该函数是为子View在ViewGroup(容器)里分配位置.
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

        int count=this.getChildCount();
        //System.out.println("l: "+l+" t:"+t+" r:"+r+" b:"+b);
        for(int i=0;i<count;i++){
            View v=getChildAt(i);
            if(v.getVisibility()!=View.GONE){
                int chlidHeight=v.getMeasuredHeight();
                //v.layout(x1,y1,x2,y2)# (x1,y1)是左上角点的坐标;(x2,y2)是右下角点的坐标.
                v.layout(l+getPaddingLeft(),t,v.getMeasuredWidth()+getPaddingLeft(),t+chlidHeight);
                //把顶点的t位置向下移
                t=t+chlidHeight;
            }
        }
    }
}

<?xml version="1.0" encoding="utf-8"?>
<yu.myprocject.MyLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="30dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="one"
        android:background="@android:color/holo_orange_light"
        android:textSize="80dip" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="two"
        android:background="@android:color/holo_red_light"
        android:textSize="80dip"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="three"
        android:background="@android:color/holo_blue_light"
        android:textSize="80dip"/>

</yu.myprocject.MyLayout>

Activity只需

setContentView(R.layout.layout_1);

25.Android手机在状态栏的消息通知

----------

参考:http://blog.sina.com.cn/s/blog_7cb2c5d50101c26t.html

android的后台运行在很多service,它们在系统启动时被SystemServer开启,支持系统的正常工作,比如MountService监听是否有SD卡安装及移除,ClipboardService提供剪切板功能,PackageManagerService提供软件包的安装移除及查看等等,应用程序可以通过系统提供的Manager接口来访问这些Service提供的数据。

getSystemService是Android很重要的一个API,它是Activity的一个方法,根据传入的NAME来取得对应的Object,然后转换成相应的服务对象。以下介绍系统相应的服务。

 传入的Name                 |         返回的对象            |        说明
WINDOW_SERVICE                    WindowManager                管理打开的窗口程序

LAYOUT_INFLATER_SERVICE           LayoutInflater               取得xml里定义的view

ACTIVITY_SERVICE                  ActivityManager              管理应用程序的系统状态

POWER_SERVICE                     PowerManger                   电源的服务

ALARM_SERVICE                     AlarmManager                  闹钟的服务

NOTIFICATION_SERVICE              NotificationManager           状态栏的服务

KEYGUARD_SERVICE                  KeyguardManager               键盘锁的服务

LOCATION_SERVICE                  LocationManager               位置的服务,如GPS

SEARCH_SERVICE                    SearchManager                 搜索的服务

VEBRATOR_SERVICE                  Vebrator                      手机震动的服务

CONNECTIVITY_SERVICE              Connectivity                  网络连接的服务

WIFI_SERVICE                      WifiManager                    Wi-Fi服务

TELEPHONY_SERVICE                 TeleponyManager                 电话服务

----------
(1)只实现状态栏上显示通知消息

public class MainActivity extends Activity {

    private final String channelId = "my channel id";
    //通知消息的id,它们把不同的消息通知区分开来。
    private int NOTIFICATION_ID=0xa01;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_1);
        sendNotification();
        /*
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        clearNotification();
        */
    }

    //发送通知消息的函数
    private void sendNotification(){
        //得到NotificationManager对象
        NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

        //对Notification的一些属性进行设置比如:内容,图标,标题,相应notification的动作进行处理等等
        NotificationCompat.Builder mBuilder=new NotificationCompat.Builder(this);
        mBuilder.setSmallIcon(R.mipmap.ic_launcher);
        mBuilder.setContentTitle("标题");
        mBuilder.setContentText("文本");
        
        //Activity跳转代码区标记
        
        Notification notification = mBuilder.build();
        //振动的效果需要增加权限
        notification.defaults = Notification.DEFAULT_SOUND|Notification.DEFAULT_VIBRATE|
                Notification.DEFAULT_LIGHTS;

        //System.currentTimeMillis()为获取当前的毫秒数
        notification.when= System.currentTimeMillis();

        //通过NotificationManager对象的notify()方法来执行一个notification的消息
        notificationManager.notify(NOTIFICATION_ID,notification);
    }

    //清除通知消息的函数
    private void clearNotification(){
        NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        //通过NotificationManager对象的cancel()方法来取消一个notificatioin的消息
        notificationManager.cancel(NOTIFICATION_ID);
    }
}
//振动的效果的权限
<uses-permission android:name="android.permission.VIBRATE"/>

(2)状态栏的通知消息点击后启动后台的Activity

将以下代码加到(1)中的//Activity跳转代码区标记位置处

        Intent notificationIntent=new Intent(this,Mac.class);
        int requestCode = 0;
        PendingIntent notificationPendingIntent = PendingIntent.getActivity(this,requestCode,notificationIntent,PendingIntent.FLAG_UPDATE_CURRENT);
        mBuilder.setContentIntent(notificationPendingIntent);

AndroidManifist.xml中加

        <activity android:name=".Mac">
        </activity>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值