Android使用Xfermode图形渲染方法实现自定义圆形、圆角和椭圆ImageView

 更新时间:2016年9月20日 19:58  点击:1512
在上一篇中我们讲了Android使用BitmapShader图形渲染实现圆形、圆角和椭圆自定义图片View,本篇我们来讲讲另外一个更为常见的图形渲染方法Xfermode。

一:简介:

在上一篇《Android使用BitmapShader图形渲染实现圆形、圆角和椭圆自定义图片View》中,采用BitmapShader方法实现自定义的圆形、圆角等自定义ImageView,这篇我们将采用更为常见的Xfermode渲染模式方案来实现圆形、圆角和椭圆样式的ImageView,同样本实例也是直接继承ImageView,

这样可以省很多事情,比如测量步骤,以及不需要自己去写设置图片的方法,本文使用Xfermode模式中的DST_IN模式来实现要达到的效果,当然大家也可以采用其他的模式,比如SRC_IN等都可以实现该效果。

(照例完整源代码在文章的最后给出下载地址哈)

二:效果图:

Android使用Xfermode图形渲染方法实现自定义圆形、圆角和椭圆ImageView

三、Xfermode渲染模式简介:

xfermode影响在Canvas已经有的图像上绘制新的颜色的方式

* 正常的情况下,在图像上绘制新的形状,如果新的Paint不是透明的,那么会遮挡下面的颜色.

* 如果新的Paint是透明的,那么会被染成下面的颜色

下面的Xfermode子类可以改变这种行为:

AvoidXfermode 指定了一个颜色和容差,强制Paint避免在它上面绘图(或者只在它上面绘图)。

PixelXorXfermode 当覆盖已有的颜色时,应用一个简单的像素XOR操作。

PorterDuffXfermode 这是一个非常强大的转换模式,使用它,可以使用图像合成的16条Porter-Duff规则的任意一条来控制Paint如何与已有的Canvas图像进行交互。

这里不得不提到那个经典的图:

Android使用Xfermode图形渲染方法实现自定义圆形、圆角和椭圆ImageView

上面的16种模式的说明如下:

从上面我们可以看到PorterDuff.Mode为枚举类,一共有16个枚举值:

1.PorterDuff.Mode.CLEAR

所绘制不会提交到画布上。

2.PorterDuff.Mode.SRC

显示上层绘制图片

3.PorterDuff.Mode.DST

显示下层绘制图片

4.PorterDuff.Mode.SRC_OVER

正常绘制显示,上下层绘制叠盖。

5.PorterDuff.Mode.DST_OVER

上下层都显示。下层居上显示。

6.PorterDuff.Mode.SRC_IN

取两层绘制交集。显示上层。

7.PorterDuff.Mode.DST_IN

取两层绘制交集。显示下层。

8.PorterDuff.Mode.SRC_OUT

取上层绘制非交集部分。

9.PorterDuff.Mode.DST_OUT

取下层绘制非交集部分。

10.PorterDuff.Mode.SRC_ATOP

取下层非交集部分与上层交集部分

11.PorterDuff.Mode.DST_ATOP

取上层非交集部分与下层交集部分

12.PorterDuff.Mode.XOR

异或:去除两图层交集部分

13.PorterDuff.Mode.DARKEN

取两图层全部区域,交集部分颜色加深

14.PorterDuff.Mode.LIGHTEN

取两图层全部,点亮交集部分颜色

15.PorterDuff.Mode.MULTIPLY

取两图层交集部分叠加后颜色

16.PorterDuff.Mode.SCREEN

取两图层全部区域,交集部分变为透明色

四、自定义圆形、圆角和椭圆的ImageView的实现

1、测量View的大小,对圆形作特殊处理

 

 

 代码如下 复制代码
/**
     * 测量view的大小
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //如果类型是圆形,则强制设置view的宽高一致,取宽高的较小值
        if(mType == TYPE_CIRCLE){
            int width = Math.min(getMeasuredWidth(),getMeasuredHeight());
            setMeasuredDimension(width, width);
        }
    }

 

 

2、绘制不同图形的Bitmap,供onDraw()绘制的时候用

 

 

 代码如下 复制代码
/**
     * 绘制不同的图形Bitmap
     */
    private Bitmap getDrawBitmap(){
        Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), 
                Bitmap.Config.ARGB_8888); 
        Canvas canvas = new Canvas(bitmap); 
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); 
        paint.setColor(Color.BLACK); 
 
        if(mType == TYPE_CIRCLE)
        {//绘制圆形
            canvas.drawCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2, 
                    paint);
        }else if(mType == TYPE_ROUND) 
        {//绘制圆角矩形
            canvas.drawRoundRect(new RectF(0, 0, getWidth(), getHeight()), 
                    mRoundBorderRadius, mRoundBorderRadius, paint); 
        }else if(mType == TYPE_OVAL ){
         //绘制椭圆
            canvas.drawOval(new RectF(0, 0, getWidth(), getHeight()), mPaint);
        }
 
        return bitmap; 
    }

 

 

3、在onDraw()中绘制出来

 

 代码如下 复制代码
/**
     * 绘制view的内容
     */
    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        //从缓存中取出bitmap
        Bitmap bmp = (mBufferBitmap == null ? null:mBufferBitmap.get());
        if(bmp == null || bmp.isRecycled()){
            //如果没有缓存存在的情况
            //获取drawable
            Drawable drawable = getDrawable();
            //获取drawable的宽高
            int dwidth = drawable.getIntrinsicWidth();
            int dheight = drawable.getIntrinsicHeight();
            Log.v("czm","dwidth="+dwidth+",width="+getWidth());
            if(null != drawable){
                bmp = Bitmap.createBitmap(getWidth(), getHeight(), 
                        Config.ARGB_8888);
                float scale = 1.0f;
                //创建画布
                Canvas drawCanvas = new Canvas(bmp);
                //按照bitmap的宽高,以及view的宽高,计算缩放比例;因为设置的src宽高
                //比例可能和imageview的宽高比例不同,这里我们不希望图片失真; 
               
                if(mType == TYPE_CIRCLE) 
                {//如果是圆形 
                    scale = getWidth() * 1.0F / Math.min(dwidth, dheight); 
                }else if (mType == TYPE_ROUND || mType == TYPE_OVAL) 
                {//如果是圆角矩形或椭圆
                    // 如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;
                    //缩放后的图片的宽高,一定要大于我们view的宽高;所以我们这里取大值; 
                    scale = Math.max(getWidth() * 1.0f / dwidth, getHeight() 
                            * 1.0f / dheight); 
                }
                Log.v("czm","scale="+scale);
                //根据缩放比例,设置bounds,即相当于做缩放图片 
                drawable.setBounds(0, 0, (int)(scale * dwidth), (int)(scale * dheight));
                drawable.draw(drawCanvas);
                //获取bitmap,即圆形、圆角或椭圆的bitmap
                if(mMaskBitmap == null || mMaskBitmap.isRecycled()){
                    mMaskBitmap = getDrawBitmap();
                }
                //为paint设置Xfermode 渲染模式
                mPaint.reset();
                mPaint.setFilterBitmap(false);
                mPaint.setXfermode(mXfermode);
                //绘制不同形状
                drawCanvas.drawBitmap(mMaskBitmap, 0, 0,mPaint);
                mPaint.setXfermode(null);
               
                //将准备好的bitmap绘制出来 
                canvas.drawBitmap(bmp, 0, 0, null); 
                //bitmap缓存起来,避免每次调用onDraw,分配内存 
                mBufferBitmap = new WeakReference<Bitmap>(bmp);
            }
           
        }else{
            //如果缓存还存在的情况
            mPaint.setXfermode(null); 
            canvas.drawBitmap(bmp, 0.0f, 0.0f, mPaint); 
            return;
        }
    }

 

 

 

4、因为使用了弱引用的缓存技术,所以需要在重写invalidate()方法中做些释放回收资源等处理:

 

 

 代码如下 复制代码
/**
     * 因为使用了缓存技术,所以需要在invalidate中做些回收释放资源的处理
     */
    @Override
    public void invalidate() {
        // TODO Auto-generated method stub
        mBufferBitmap = null;
        if(mMaskBitmap != null){
            mMaskBitmap.recycle();
            mMaskBitmap = null;
        }
        super.invalidate();
    }

 

 

五、视图布局的实现:

 

 

 代码如下 复制代码

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
     >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="55dp"
        android:orientation="vertical" >

        <com.czm.myroundimageview.XCRoundImageViewByXfermode
            android:id="@+id/cicleImageView"
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:src="@drawable/img1" />

        <com.czm.myroundimageview.XCRoundImageViewByXfermode
            android:id="@+id/roundRectImageView"
            android:layout_width="125dp"
            android:layout_height="145dp"
            android:layout_marginTop="15dp"
            android:src="@drawable/img2" />

        <com.czm.myroundimageview.XCRoundImageViewByXfermode
            android:id="@+id/ovalImageView"
            android:layout_width="140dp"
            android:layout_height="184dp"
            android:layout_marginTop="15dp"
            android:src="@drawable/img3" />
    </LinearLayout>

</ScrollView>

 

 

六、使用和测试自定义ImageView

上面直接绘制的自定义ImageView写完了,下面就是使用这个自定义的ImageView了,使用方法和普通的ImageView一样,当作普通控件使用即可。

 

 

 代码如下 复制代码

package com.czm.myroundimageview;

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {

    private XCRoundImageViewByXfermode circleImageView;//圆形图片
    private XCRoundImageViewByXfermode roundRectImageView;//圆角矩形图片
    private XCRoundImageViewByXfermode ovalImageView;//椭圆图片
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
    }
    /**
     * 初始化Views
     */
    private void initViews(){
        circleImageView = (XCRoundImageViewByXfermode)findViewById(R.id.cicleImageView);
        roundRectImageView = (XCRoundImageViewByXfermode)findViewById(R.id.roundRectImageView);
        ovalImageView = (XCRoundImageViewByXfermode)findViewById(R.id.ovalImageView);
       
        roundRectImageView.setType(XCRoundImageViewByXfermode.TYPE_ROUND);
        roundRectImageView.setRoundBorderRadius(100);
       
        ovalImageView.setType(XCRoundImageViewByXfermode.TYPE_OVAL);
        ovalImageView.setRoundBorderRadius(50);
       
    }

}

 

 

现在的智能手机都是触屏的,现在我们来学习一下Android开发的Touch(触屏)事件,看了本文章后你可以在30分钟之内快速明白Touch事件分发机制。

Touch事件分发中只有两个主角:ViewGroup和View。Activity的Touch事件事实上是调用它内部的ViewGroup的Touch事件,可以直接当成ViewGroup处理。

View在ViewGroup内,ViewGroup也可以在其他ViewGroup内,这时候把内部的ViewGroup当成View来分析。

ViewGroup的相关事件有三个:onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent。View的相关事件只有两个:dispatchTouchEvent、onTouchEvent。

先分析 ViewGroup的处理流程:首先得有个结构模型概念:ViewGroup和View组成了一棵树形结构,最顶层为Activity的 ViewGroup,下面有若干的ViewGroup节点,每个节点之下又有若干的ViewGroup节点或者View节点,依次类推。如图:

快速掌握Android开发中Touch事件分发机制

当一个 Touch事件(触摸事件为例)到达根节点,即Acitivty的ViewGroup时,它会依次下发,下发的过程是调用子 View(ViewGroup)的dispatchTouchEvent方法实现的。简单来说,就是ViewGroup遍历它包含着的子View,调用每个View的dispatchTouchEvent方法,而当子View为ViewGroup时,又会通过调用ViwGroup的 dispatchTouchEvent方法继续调用其内部的View的dispatchTouchEvent方法。上述例子中的消息下发顺序是这样的:①-②-⑤-⑥-⑦-③-④。dispatchTouchEvent方法只负责事件的分发,它拥有boolean类型的返回值,当返回为true时,顺序下发会中断。在上述例子中如果⑤的dispatchTouchEvent返回结果为true,那么⑥-⑦-③-④将都接收不到本次Touch事件。来个简单版的代码加深理解:

 

 代码如下 复制代码
/**
     * ViewGroup
     * @param ev
     * @return
     */
    public boolean dispatchTouchEvent(MotionEvent ev){
        ....//其他处理,在此不管
        View[] views=getChildView();
        for(int i=0;i<views.length;i++){
           //判断下Touch到屏幕上的点在该子View上面
            if(...){
            if(views[i].dispatchTouchEvent(ev))
              return true;
             }
        }
        ...//其他处理,在此不管
    }
    /**
     * View
     * @param ev
     * @return
     */
    public boolean dispatchTouchEvent(MotionEvent ev){
        ....//其他处理,在此不管
        return false;
    }

 

在此可以看出,ViewGroup的dispatchTouchEvent是真正在执行“分发”工作,而View的dispatchTouchEvent方法,并不执行分发工作,或者说它分发的对象就是自己,决定是否把touch事件交给自己处理,而处理的方法,便是onTouchEvent事件,事实上子View 的dispatchTouchEvent方法真正执行的代码是这样的

 

 代码如下 复制代码
/**
     * View
     * @param ev
     * @return
     */
    public boolean dispatchTouchEvent(MotionEvent ev){
        ....//其他处理,在此不管
        return onTouchEvent(event);
    }

 

一般情况下,我们不该在普通View内重写dispatchTouchEvent方法,因为它并不执行分发逻辑。当Touch事件到达View时,我们该做的就是是否在onTouchEvent事件中处理它。

那么,ViewGroup的onTouchEvent事件是什么时候处理的呢?当ViewGroup所有的子View都返回false 时,onTouchEvent事件便会执行。由于ViewGroup是继承于View的,它其实也是通过调用View的 dispatchTouchEvent方法来执行onTouchEvent事件。

在目前的情况看来,似乎只要我们把所有的onTouchEvent都返回false,就能保证所有的子控件都响应本次Touch事件了。但必须要说明的是,这里的 Touch事件,只限于Acition_Down事件,即触摸按下事件,而Aciton_UP和Action_MOVE却不会执行。事实上,一次完整的 Touch事件,应该是由一个Down、一个Up和若干个Move组成的。Down方式通过dispatchTouchEvent分发,分发的目的是为了找到真正需要处理完整Touch请求的View。当某个View或者ViewGroup的onTouchEvent事件返回true时,便表示它是真正要处理这次请求的View,之后的Aciton_UP和Action_MOVE将由它处理。当所有子View的onTouchEvent都返回false 时,这次的Touch请求就由根ViewGroup,即Activity自己处理了。

看看改进后的ViewGroup的dispatchTouchEvent方法

 

 代码如下 复制代码

View mTarget=null;//保存捕获Touch事件处理的View
    public boolean dispatchTouchEvent(MotionEvent ev) {

        //....其他处理,在此不管
       
        if(ev.getAction()==KeyEvent.ACTION_DOWN){
            //每次Down事件,都置为Null

            if(!onInterceptTouchEvent()){
            mTarget=null;
            View[] views=getChildView();
            for(int i=0;i<views.length;i++){
                if(views[i].dispatchTouchEvent(ev))
                    mTarget=views[i];
                    return true;
            }
          }
        }
        //当子View没有捕获down事件时,ViewGroup自身处理。这里处理的Touch事件包含Down、Up和Move
        if(mTarget==null){
            return super.dispatchTouchEvent(ev);
        }
        //...其他处理,在此不管
        if(onInterceptTouchEvent()){

         //...其他处理,在此不管   
         }
//这一步在Action_Down中是不会执行到的,只有Move和UP才会执行到。
        return mTarget.dispatchTouchEvent(ev);

    }

 

ViewGroup还有个onInterceptTouchEvent,看名字便知道这是个拦截事件。这个拦截事件需要分两种情况来说明:

1.假如我们在某个ViewGroup的onInterceptTouchEvent中,将Action为Down的Touch事件返回true,那便表示将该 ViewGroup的所有下发操作拦截掉,这种情况下,mTarget会一直为null,因为mTarget是在Down事件中赋值的。由于mTarge 为null,该ViewGroup的onTouchEvent事件被执行。这种情况下可以把这个ViewGroup直接当成View来对待。

2.假如我们在某个ViewGroup的onInterceptTouchEvent中,将Acion为Down的Touch事件都返回false,其他的都返回 True,这种情况下,Down事件能正常分发,若子View都返回false,那mTarget还是为空,无影响。若某个子View返回了 true,mTarget被赋值了,在Action_Move和Aciton_UP分发到该ViewGroup时,便会给mTarget分发一个 Action_Delete的MotionEvent,同时清空mTarget的值,使得接下去的Action_Move(如果上一个操作不是UP)将由 ViewGroup的onTouchEvent处理。

情况一用到的比较多,情况二个人还未找到使用场景。

从头到尾总结一下:

1.Touch 事件分发中只有两个主角:ViewGroup和View。ViewGroup包含onInterceptTouchEvent、 dispatchTouchEvent、onTouchEvent三个相关事件。View包含dispatchTouchEvent、 onTouchEvent两个相关事件。其中ViewGroup又继承于View。

2.ViewGroup和View组成了一个树状结构,根节点为Activity内部包含的一个ViwGroup。

3.触摸事件由Action_Down、Action_Move、Aciton_UP组成,其中一次完整的触摸事件中,Down和Up都只有一个,Move有若干个,可以为0个。

4.当Acitivty接收到Touch事件时,将遍历子View进行Down事件的分发。ViewGroup的遍历可以看成是递归的。分发的目的是为了找到真正要处理本次完整触摸事件的View,这个View会在onTouchuEvent结果返回true。

5.当某个子 View返回true时,会中止Down事件的分发,同时在ViewGroup中记录该子View。接下去的Move和Up事件将由该子View直接进行处理。由于子View是保存在ViewGroup中的,多层ViewGroup的节点结构时,上级ViewGroup保存的会是真实处理事件的View所在的ViewGroup对象:如ViewGroup0-ViewGroup1-TextView的结构中,TextView返回了true,它将被保存在 ViewGroup1中,而ViewGroup1也会返回true,被保存在ViewGroup0中。当Move和UP事件来时,会先从 ViewGroup0传递至ViewGroup1,再由ViewGroup1传递至TextView。

6.当 ViewGroup中所有子View都不捕获Down事件时,将触发ViewGroup自身的onTouch事件。触发的方式是调用 super.dispatchTouchEvent函数,即父类View的dispatchTouchEvent方法。在所有子View都不处理的情况下,触发Acitivity的onTouchEvent方法。

7.onInterceptTouchEvent有两个作用:1.拦截Down事件的分发。2.中止Up和Move事件向目标View传递,使得目标View所在的ViewGroup捕获Up和Move事件。

另外,上文所列出的代码并非真正的源码,只是概括了源码在事件分发处理中的核心处理流程,真正源码各位可以自己去看,包含了更丰富的内容。

圆角图片在Android开发中比较常用,现在我们来介绍在Android如何实现圆形、圆角和椭圆自定义图片View,这时主要是使用BitmapShader图形渲染。

一、概述

Android实现圆角矩形,圆形或者椭圆等图形,一般主要是个自定义View加上使用Xfermode实现的。实现圆角图片的方法其实不少,常见的就是利用Xfermode,Shader。本文直接继承ImageView,使用BitmapShader方法来实现圆形、圆角和椭圆的绘制,等大家看我本文的方法后,其他的类似形状也就都能举一反三来来画出来了。

二、效果图:

Android使用BitmapShader图形渲染实现圆形、圆角和椭圆自定义图片View

三、BitmapShader简介

BitmapShader是Shader的子类,可以通过Paint.setShader(Shader shader)进行设置、

我们这里只关注BitmapShader,构造方法:

mBitmapShader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP);

参数1:bitmap

参数2,参数3:TileMode;

TileMode的取值有三种:

CLAMP 拉伸

REPEAT 重复

MIRROR 镜像

如果大家给电脑屏幕设置屏保的时候,如果图片太小,可以选择重复、拉伸、镜像;

重复:就是横向、纵向不断重复这个bitmap

镜像:横向不断翻转重复,纵向不断翻转重复;

拉伸:这个和电脑屏保的模式应该有些不同,这个拉伸的是图片最后的那一个像素;横向的最后一个横行像素,不断的重复,纵项的那一列像素,不断的重复;

public BitmapShader(Bitmap bitmap,Shader.TileMode tileX,Shader.TileMode tileY)

调用这个方法来产生一个画有一个位图的渲染器(Shader)。

bitmap 在渲染器内使用的位图

tileX The tiling mode for x to draw the bitmap in. 在位图上X方向花砖模式

tileY The tiling mode for y to draw the bitmap in. 在位图上Y方向花砖模式

TileMode:(一共有三种)

CLAMP :如果渲染器超出原始边界范围,会复制范围内边缘染色。

REPEAT :横向和纵向的重复渲染器图片,平铺。

MIRROR :横向和纵向的重复渲染器图片,这个和REPEAT 重复方式不一样,他是以镜像方式平铺。

四、自定义圆形、圆角和椭圆的图片View的实现

1. 测量View的大小

 

 代码如下 复制代码

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 如果是绘制圆形,则强制宽高大小一致
        if (mType == TYPE_CIRCLE) {
            mWidth = Math.min(getMeasuredWidth(), getMeasuredHeight());
            mRadius = mWidth / 2;
            setMeasuredDimension(mWidth, mWidth);
        }

    }

 

2、设置BitmapShader和画笔Paint

 

 代码如下 复制代码

/**
     * 设置BitmapShader
     */
    private void setBitmapShader() {
        Drawable drawable = getDrawable();
        if (null == drawable) {
            return;
        }
        Bitmap bitmap = drawableToBitmap(drawable);
        // 将bitmap作为着色器来创建一个BitmapShader
        mBitmapShader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP);
        float scale = 1.0f;
        if (mType == TYPE_CIRCLE) {
            // 拿到bitmap宽或高的小值
            int bSize = Math.min(bitmap.getWidth(), bitmap.getHeight());
            scale = mWidth * 1.0f / bSize;

        } else if (mType == TYPE_ROUND || mType == TYPE_OVAL) {
            // 如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;缩放后的图片的宽高,一定要大于我们view的宽高;所以我们这里取大值;
            scale = Math.max(getWidth() * 1.0f / bitmap.getWidth(), getHeight() * 1.0f / bitmap.getHeight());
        }
        // shader的变换矩阵,我们这里主要用于放大或者缩小
        mMatrix.setScale(scale, scale);
        // 设置变换矩阵
        mBitmapShader.setLocalMatrix(mMatrix);
        mPaint.setShader(mBitmapShader);

    }

 

 

3.最后就是绘制出来圆角、圆形和椭圆的图片,肯定在onDraw里面啦,根本原理就是使用了上面mBitmapShader渲染的画笔来绘制

 

 代码如下 复制代码

@Override
    protected void onDraw(Canvas canvas) {

        if (null == getDrawable()) {
            return;
        }
        setBitmapShader();
        if (mType == TYPE_CIRCLE) {
            canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
        } else if (mType == TYPE_ROUND) {
            mPaint.setColor(Color.RED);
            canvas.drawRoundRect(mRect, mRoundRadius, mRoundRadius, mPaint);
        }else if(mType == TYPE_OVAL){
            canvas.drawOval(mRect, mPaint);
        }
    }

 

 

五、视图布局实现

这个很简单,就是3个自定义的view:

 

 

 代码如下 复制代码

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:layout_marginTop="5dp"
        android:layout_marginBottom="25dp"
        android:orientation="vertical" >

        <com.czm.viewdrawtest.XCRoundAndOvalImageView
            android:id="@+id/cicleImageView"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:src="@drawable/img1" />

        <com.czm.viewdrawtest.XCRoundAndOvalImageView
            android:id="@+id/roundRectImageView"
            android:layout_width="200dp"
            android:layout_height="240dp"
            android:layout_marginTop="5dp"
            android:src="@drawable/img2" />

        <com.czm.viewdrawtest.XCRoundAndOvalImageView
            android:id="@+id/ovalImageView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:src="@drawable/img3" />
    </LinearLayout>

</ScrollView>

 

六、使用和测试自定义View

上面直接绘制的自定义View写完了,下面就是使用这个View了,使用方法和普通的ImageView一样,当作普通控件使用即可。

 

 

 代码如下 复制代码

package com.czm.viewdrawtest;


import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
/**
 * 使用自定义ImageView
 * @author caizhiming
 *
 */
public class MainActivity extends Activity {

    private XCRoundAndOvalImageView circleImageView;//圆形图片
    private XCRoundAndOvalImageView roundRectImageView;//圆角矩形图片
    private XCRoundAndOvalImageView ovalImageView;//椭圆图片
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //设置无标题 
        requestWindowFeature(Window.FEATURE_NO_TITLE); 
        //设置全屏 
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,  
                WindowManager.LayoutParams.FLAG_FULLSCREEN); 
        setContentView(R.layout.activity_main);
       
        initViews();
    }
    /**
     * 初始化Views
     */
    private void initViews(){
        circleImageView = (XCRoundAndOvalImageView)findViewById(R.id.cicleImageView);
        roundRectImageView = (XCRoundAndOvalImageView)findViewById(R.id.roundRectImageView);
        ovalImageView = (XCRoundAndOvalImageView)findViewById(R.id.ovalImageView);
       
        roundRectImageView.setType(XCRoundAndOvalImageView.TYPE_ROUND);
        roundRectImageView.setRoundRadius(100);
       
        ovalImageView.setType(XCRoundAndOvalImageView.TYPE_OVAL);
        ovalImageView.setRoundRadius(50);
       
    }
}

 

以下是本人整理出来的几篇关于Android Studio开发环境使用的教程,希望您通过这几篇文章的学习,能够掌握Android开发神器Android Studio的使用

[图文]Android Studio下载安装及配置开发环境教程
http://www.111cn.net/sj/android/74261.htm

win7X64下Android Stuido安装及搭建开发环境问题解决
http://www.111cn.net/sj/android/74265.htm

mac下载安装Android Studio配置开发环境教程
http://www.111cn.net/sj/android/74262.htm

总结android studio注意事项及打不开等问题解决方法
http://www.111cn.net/sj/android/74264.htm

小结Android Studio常用设置说明教程
http://www.111cn.net/sj/android/74263.htm

总结Android Studio导入项目的几种方法
http://www.111cn.net/sj/android/74260.htm

[最新图文版]如何将项目从eclipse迁移到android studio?
http://www.111cn.net/sj/android/74224.htm

在开发Android应用教程时,有时会让Android自动重启关机,如何用JAVA实现呢?现在我们来分享一段实现Android关机重启的JAVA代码。

实现系统重启的APK需要system的权限,在AndroidManifest.xml中增加android:sharedUserId="android.uid.system",再修改签名即可;

具体方法参考:

点击打开链接

1、使用PowerManager来实现:

代码:

private void rebootSystem(){
    PowerManager pManager=(PowerManager) getSystemService(Context.POWER_SERVICE);
    pManager.reboot("");
}

2、发送REBOOT广播:

代码:

private void rebootSystem(){
Intent reboot = new Intent(Intent.ACTION_REBOOT);
reboot.putExtra("nowait", 1);
reboot.putExtra("interval", 1);
reboot.putExtra("window", 0);
sendBroadcast(reboot);
}

[!--infotagslink--]

相关文章

  • 图解PHP使用Zend Guard 6.0加密方法教程

    有时为了网站安全和版权问题,会对自己写的php源码进行加密,在php加密技术上最常用的是zend公司的zend guard 加密软件,现在我们来图文讲解一下。 下面就简单说说如何...2016-11-25
  • Android子控件超出父控件的范围显示出来方法

    下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
  • ps怎么使用HSL面板

    ps软件是现在很多人都会使用到的,HSL面板在ps软件中又有着非常独特的作用。这次文章就给大家介绍下ps怎么使用HSL面板,还不知道使用方法的下面一起来看看。 &#8195;...2017-07-06
  • C#创建自定义控件及添加自定义属性和事件使用实例详解

    这篇文章主要给大家介绍了关于C#创建自定义控件及添加自定义属性和事件使用的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用C#具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧...2020-06-25
  • JS实现自定义简单网页软键盘效果代码

    本文实例讲述了JS实现自定义简单网页软键盘效果。分享给大家供大家参考,具体如下:这是一款自定义的简单点的网页软键盘,没有使用任何控件,仅是为了练习JavaScript编写水平,安全性方面没有过多考虑,有顾虑的可以不用,目的是学...2015-11-08
  • Plesk控制面板新手使用手册总结

    许多的朋友对于Plesk控制面板应用不是非常的了解特别是英文版的Plesk控制面板,在这里小编整理了一些关于Plesk控制面板常用的使用方案整理,具体如下。 本文基于Linu...2016-10-10
  • 使用insertAfter()方法在现有元素后添加一个新元素

    复制代码 代码如下: //在现有元素后添加一个新元素 function insertAfter(newElement, targetElement){ var parent = targetElement.parentNode; if (parent.lastChild == targetElement){ parent.appendChild(newEl...2014-05-31
  • Android开发中findViewById()函数用法与简化

    findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20
  • Android模拟器上模拟来电和短信配置

    如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
  • jQuery 1.9使用$.support替代$.browser的使用方法

    jQuery 从 1.9 版开始,移除了 $.browser 和 $.browser.version , 取而代之的是 $.support 。 在更新的 2.0 版本中,将不再支持 IE 6/7/8。 以后,如果用户需要支持 IE 6/7/8,只能使用 jQuery 1.9。 如果要全面支持 IE,并混合...2014-05-31
  • 使用GruntJS构建Web程序之构建篇

    大概有如下步骤 新建项目Bejs 新建文件package.json 新建文件Gruntfile.js 命令行执行grunt任务 一、新建项目Bejs源码放在src下,该目录有两个js文件,selector.js和ajax.js。编译后代码放在dest,这个grunt会...2014-06-07
  • 使用percona-toolkit操作MySQL的实用命令小结

    1.pt-archiver 功能介绍: 将mysql数据库中表的记录归档到另外一个表或者文件 用法介绍: pt-archiver [OPTION...] --source DSN --where WHERE 这个工具只是归档旧的数据,不会对线上数据的OLTP查询造成太大影响,你可以将...2015-11-24
  • 如何使用php脚本给html中引用的js和css路径打上版本号

    在搜索引擎中搜索关键字.htaccess 缓存,你可以搜索到很多关于设置网站文件缓存的教程,通过设置可以将css、js等不太经常更新的文件缓存在浏览器端,这样访客每次访问你的网站的时候,浏览器就可以从浏览器的缓存中获取css、...2015-11-24
  • C#注释的一些使用方法浅谈

    C#注释的一些使用方法浅谈,需要的朋友可以参考一下...2020-06-25
  • 夜神android模拟器设置代理的方法

    夜神android模拟器如何设置代理呢?对于这个问题其实操作起来是非常的简单,下面小编来为各位详细介绍夜神android模拟器设置代理的方法,希望例子能够帮助到各位。 app...2016-09-20
  • android自定义动态设置Button样式【很常用】

    为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
  • MySQL日志分析软件mysqlsla的安装和使用教程

    一、下载 mysqlsla [root@localhost tmp]# wget http://hackmysql.com/scripts/mysqlsla-2.03.tar.gz--19:45:45-- http://hackmysql.com/scripts/mysqlsla-2.03.tar.gzResolving hackmysql.com... 64.13.232.157Conn...2015-11-24
  • Android WebView加载html5页面实例教程

    如果我们要在Android应用APP中加载html5页面,我们可以使用WebView,本文我们分享两个WebView加载html5页面实例应用。 实例一:WebView加载html5实现炫酷引导页面大多...2016-09-20
  • 安装和使用percona-toolkit来辅助操作MySQL的基本教程

    一、percona-toolkit简介 percona-toolkit是一组高级命令行工具的集合,用来执行各种通过手工执行非常复杂和麻烦的mysql和系统任务,这些任务包括: 检查master和slave数据的一致性 有效地对记录进行归档 查找重复的索...2015-11-24
  • 深入理解Android中View和ViewGroup

    深入理解Android中View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的。View是基类,ViewGroup是它的子类。本教程我们深...2016-09-20