简单几步教你实现对 Drawable 的扇形区域剪切显示

大家如果喜欢我的博客,请关注一下我的微博,请点击这里(http://weibo.com/kifile),谢谢

转载请标明出处(http://blog.csdn.net/kifile),再次感谢

在开发过程中,单纯的 Drawable 文件无法满足我们对整个项目的需求.

有时候在制作过场动画的时候,我们会希望将一个 Drawable 文件以中心为基准,按顺时针慢慢显示出来,可是 Android 并没有为我们提供一个工具类,我们也不希望为了单纯的显示整张图片而去制作 N 张图片以满足过场动画的需求,那么我们这个时候只能考虑对 Drawable 的绘画区域做裁剪,让他只显示扇形区域的大小,以满足我们的需求.

幸而, Android 本身有一个 ClipDrawable 类,这个类让我们能够轻松的显示进度条加载进度,本次我们也将根据这个类来创建一个类似的代码

先送上具体源码,然后我们会详细分析一下裁剪显示区域的原理

/*
 * Copyright (C) 2014 Kifile([email protected])
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.kifile.graphics;

import android.content.res.ColorStateList;
import android.graphics.*;
import android.graphics.drawable.Drawable;

/**
 * Created by kifile on 14/10/31.
 */
public class SectorDrawable extends Drawable implements Drawable.Callback {
    private Drawable mDrawable;
    private Path mPath = new Path();
    private float mPercent;

    public SectorDrawable(Drawable drawable) {
        this.mDrawable = drawable;
        if (drawable != null) {
            drawable.setCallback(this);
        }
    }

    @Override
    public int getChangingConfigurations() {
        return super.getChangingConfigurations() | mDrawable.getChangingConfigurations();
    }

    @Override
    public boolean getPadding(Rect padding) {
        return mDrawable.getPadding(padding);
    }

    @Override
    public boolean setVisible(boolean visible, boolean restart) {
        mDrawable.setVisible(visible, restart);
        return super.setVisible(visible, restart);
    }

    @Override
    public void draw(Canvas canvas) {
        mPath.reset();
        RectF rect = new RectF(getBounds());
        double radius = Math.pow(Math.pow(rect.right, 2) + Math.pow(rect.bottom, 2), 0.5);
        mPath.moveTo(rect.right / 2, rect.bottom / 2);
        mPath.lineTo(rect.right / 2, 0);
        if (mPercent > 0.125f) {
            mPath.lineTo(rect.right, 0);
        }
        if (mPercent > 0.375f) {
            mPath.lineTo(rect.right, rect.bottom);
        }
        if (mPercent > 0.625f) {
            mPath.lineTo(0, rect.bottom);
        }
        if (mPercent > 0.875f) {
            mPath.lineTo(0, 0);
        }
        mPath.lineTo((float) (rect.right / 2 + radius * Math.sin(Math.PI * 2 * mPercent)),
                (float) (rect.bottom / 2 - radius * Math.cos(Math.PI * 2 * mPercent)));
        mPath.close();
        if (mPercent >= 0 && mPercent <= 1) {
            canvas.save();
            canvas.clipPath(mPath);
            mDrawable.draw(canvas);
            canvas.restore();
        }
    }

    @Override
    public void setAlpha(int alpha) {
        mDrawable.setAlpha(alpha);
    }

    @Override
    public int getAlpha() {
        return mDrawable.getAlpha();
    }

    @Override
    public void setColorFilter(ColorFilter cf) {
        mDrawable.setColorFilter(cf);
    }

    @Override
    public void setTintList(ColorStateList tint) {
        mDrawable.setTintList(tint);
    }

    @Override
    public void setTintMode(PorterDuff.Mode tintMode) {
        mDrawable.setTintMode(tintMode);
    }

    @Override
    public int getOpacity() {
        // TODO Auto-generated method stub
        return mDrawable.getOpacity();
    }

    @Override
    public boolean isStateful() {
        // TODO Auto-generated method stub
        return mDrawable.isStateful();
    }

    @Override
    protected boolean onStateChange(int[] state) {
        return mDrawable.setState(state);
    }

    @Override
    protected boolean onLevelChange(int level) {
        mDrawable.setLevel(level);
        invalidateSelf();
        return true;
    }

    @Override
    protected void onBoundsChange(Rect bounds) {
        mDrawable.setBounds(bounds);
    }

    @Override
    public int getIntrinsicHeight() {
        return mDrawable.getIntrinsicHeight();
    }

    @Override
    public int getIntrinsicWidth() {
        return mDrawable.getIntrinsicWidth();
    }

    /**
     * 显示的区域范围
     *
     * @param percent 0至1
     */
    public void setPercent(float percent) {
        if (percent > 1) {
            percent = 1;
        } else if (percent < 0) {
            percent = 0;
        }
        if (percent != mPercent) {
            this.mPercent = percent;
            invalidateSelf();
        }
    }

    @Override
    public void invalidateDrawable(Drawable who) {
        final Callback callback = getCallback();
        if (callback != null) {
            callback.invalidateDrawable(this);
        }
    }

    @Override
    public void scheduleDrawable(Drawable who, Runnable what, long when) {
        final Callback callback = getCallback();
        if (callback != null) {
            callback.scheduleDrawable(this, what, when);
        }
    }

    @Override
    public void unscheduleDrawable(Drawable who, Runnable what) {
        final Callback callback = getCallback();
        if (callback != null) {
            callback.unscheduleDrawable(this, what);
        }
    }

}

从上面的代码可以看出,我们使用了装饰者模式来处理本类,首先我们在构造函数中传入一个实际的 Drawable 对象,并将各种事务交给了 Drawable 对象进行处理,我们只负责对 draw 方法的重写,所以我们可以好好来看看 draw 方法.

首先大家先来看一张图:

黑色部分为画布区域, w 为宽度, h 为高度, radius 为中心点到角的距离,上图中为我们标明了9个重要的坐标点位置,接下来我们将介绍如何根据旋转角度来设置选定区域范围

首先我们先规定扇形区域的起始位置为(w/2,0)处,旋转方式为顺时针旋转,并假设有一 A 点为扇形旋转区域另一边的,长度为 radius(关于 radius 的定义请参看上面) 的边角

(1)当旋转区域不超过1/8时,扇形区域的绘制如下:

那么由图可知,我们需要裁减的区域为上图中的蓝色区域即可

(2)对于旋转区域超过1/8,不超过3/8时,扇形区域绘制如下:

由图可知,我们需要裁减的蓝色区域,可由,中心点,起始点,右上角和 A 点连线组成

(3)对于旋转区域超过3/8不超过5/8时,扇形区域绘制如下:

由图可知,我们需要裁减的蓝色区域,可由,中心点,起始点,右上角,右下角和 A 点连线组成

(4)对于超过5/8,不超过7/8的部分,裁剪区域如下:

(5)对于超过7/8的部分而言,裁减区域如下

因此我们可以通过判断设定的显示区域,动态对画布进行裁减,以达到显示扇形区域的目的

具体的设置代码,就在顶部,大家如果有兴趣可以详细看看,接下来我们将查看如何正确使用 SectorDrawable

ImageView img = (ImageView) findViewById(R.id.sector_img);
        mDrawable = new SectorDrawable(img.getDrawable());
        img.setImageDrawable(mDrawable);

在这段代码中,我们从 ImageView 中获取了一个 Drawable 对象,然后使用 SectorDrawable 来装饰他,然后将 setctorDrawable, 再设置到 ImageView 中

当我们需要调用代码进行区域显示设置时,使用

mDrawable.setPercent(percent);

详细的一个 Activity 示例如下:

/*
 * Copyright (C) 2014 Kifile([email protected])
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.kifile.sample.app;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ImageView;
import com.kifile.graphics.SectorDrawable;

public class MainActivity extends ActionBarActivity {
    private SectorDrawable mDrawable;

    private Handler mHandler = new Handler() {
        private float percent;

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (percent <= 1) {
                percent += 0.01;
            } else {
                percent = 0;
                return;
            }
            mDrawable.setPercent(percent);
            Log.i("this",String.valueOf(percent));
            sendEmptyMessageDelayed(0, 10);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ImageView img = (ImageView) findViewById(R.id.sector_img);
        mDrawable = new SectorDrawable(img.getDrawable());
        img.setImageDrawable(mDrawable);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            mHandler.sendEmptyMessage(0);
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

}

这段代码将在点击菜单按钮的时候触发事件,使用 handler 不断刷新显示区域,起到扇形区域显示的目的.

好了,本次博客就到这里了,谢谢大家的翻阅

时间: 2024-10-12 12:08:45

简单几步教你实现对 Drawable 的扇形区域剪切显示的相关文章

简单几步教你如何将手机投屏到电视,1秒小屏变大屏

不知道大家有没有一种手机看电影,看视频,屏幕限制太大,总觉得好像少了点东西? 修姐,就是!总是赶紧我好像少看了一些画面,让你瞬间体会满屏的效果! 简单几步教你如何将手机投屏到电视,1秒小屏变大屏!高中之前一直看的电视,投影电影等:大学也是躺在宿舍用电脑刷剧:演变到现在基本上都是手机刷,电影院也是很少跑.可是手机屏幕还是太小了,很多画面看着总感觉不够震撼,少点感官需求! 但是把手机上的画面投到电视上,不论是看剧还是打游戏,那个体验一定是很爽!既享受到了手机的丰富资源和便利,又可以享受电视高清大屏的

简单4步教你用在线表格设计用户调研收集表

用在线表格设计用户调研收集表?在线表格还可以做这个?大家肯定有很多疑问,今天小编就来给你一一解答,只需要简单的4步,就可轻松设计一套用户调研收集表. 1:我们使用百度浏览器搜索"蜘蛛表格"官网,进入官网点击免费使用或者右上角注册 2:点击注册之后弹如入第二个页面,完成以下填写信息之后注册,会提示你注册成功,登录之后会进入后台. 3:然后新建应用-新建模板应用-用户调研模板-使用模板-修改相关参数及调研问题. 4:相关参数设置完成点击发布表单-创建生成链接,用户调研系统就做好了,可分享链

简单11步教你部署ESXi5.5

附件:http://pan.baidu.com/s/1o6C423w 提取码(刮开):ycww 1.第一步.光驱启动,进入到如下画面(硬件兼容性说明),按下回车继续 2.第二步.按下F11同意许可协议 3.第三步.扫描系统硬盘,按下回车确认选择 4.第四步.选择键盘布局 5.第五步.输入root账户密码 6.第六步.按下F11确认安装 正在复制安装文件,安装过程启动,大概2分钟左右 7.第七步.安装完成,按下回车重启系统 8.第八步.重启后进入如下界面,按下F2 9.第九步.按下F2,输入ROO

PHP实现对mysql数据库内容分页显示

<?php $conn=mysql_connect('127.0.0.1','root',''); mysql_query('use test',$conn); mysql_query('set names utf8',$conn); $perNumber=3; //每页显示的记录数 $page=$_GET['page']; //获得当前的页面值 $count=mysql_query("select count(*) from kangbiao"); //获得记录总数 $rs=m

简单两步让博客园支持手机端显示

博客园的模板是没有兼容手机端显示的,阅读体验比较差.本文教你如何简单几步让你的博客支持手机端显示.找一个夜深人静,没有人浏览你博客的时间点,开始吧. 1.添加js代码 在博客园后台的“设置”菜单下,有一项页首Html代码,此处写js代码也是可以生效的,将如下代码复制过去: <script> var content = 'width=device-width, initial-scale=1 user-scalable=no'; var viewport = document.createEle

不用splitter控件 简单实现对mfc对话框的分割的方法

不用splitter控件  简单实现对mfc对话框的分割的方法 直接贴上源代码主要部分吧 这个是基于对话框的工程 进行对话框的分割实现 只是相应了三个消息函数,看一下就会明白的 我空间资源里边有现成的工程代码可以下载运行 .cpp 文件 [cpp] view plaincopy // spliteDlg.cpp : implementation file // #include "stdafx.h" #include "splite.h" #include &quo

Android下通过root实现对system_server中binder的ioctl调用拦截

Android下通过root实现对system_server中binder的ioctl调用拦截 分类: Android2013-06-19 18:09 779人阅读 评论(0) 收藏 举报 作 者: Passion时 间: 2012-10-18,13:53:53链 接: http://bbs.pediy.com/showthread.php?t=157419 Android下通过root实现对system_server中binder的ioctl调用拦截作者:passion2012-10-18关键

Zabbix配合fpmmm(mpm)实现对Mysql的全面监控

Zabbix使用fpmmm(mpm)模块实现对Mysql的全面监控(Performance Monitor for MySQL) fpmmm is the FromDualPerformance Monitor for MySQL and MariaDB and the counterpart to the MySQL EnterpriseMonitor from Oracle.With fpmmm you can monitor your MySQL/MariaDB databases and

用 Python 脚本实现对 Linux 服务器的监控

hon 分享到:8 原文出处: 曹江华 目前 Linux 下有一些使用 Python 语言编写的 Linux 系统监控工具 比如 inotify-sync(文件系统安全监控软件).glances(资源监控工具)在实际工作中,Linux 系统管理员可以根据自己使用的服务器的具体情况编写一下简单实用的脚本实现对 Linux 服务器的监控. 本文介绍一下使用 Python 脚本实现对 Linux 服务器 CPU 内存 网络的监控脚本的编写. Python 版本说明 Python 是由 Guido va