FrameLayout(帧布局)
常用属性
FrameLayout的属性很少就两个,但是在说之前我们先介绍一个东西:
前景图像:永远处于帧布局最上面,直接面对用户的图像,就是不会被覆盖的图片。
两个属性:
- android:foreground:*设置改帧布局容器的前景图像
- android:foregroundGravity:设置前景图像显示的位置
TableLayout(表格布局)
常用属性
android:collapseColumns:设置需要被隐藏的列的序号
android:shrinkColumns:设置允许被收缩的列的列序号
android:stretchColumns:设置运行被拉伸的列的列序号
以上这三个属性的列号都是从0开始算的,比如shrinkColunmns ="2",对应的是第三列!
可以设置多个,用逗号隔开比如"0,2",如果是所有列都生效,则用"*"号即可
除了这三个常用属性,还有两个属性,分别就是跳格子以及合并单元格,这和HTML中的Table类似:
android:layout_column="2":表示的就是跳过第二个,直接显示到第三个格子处,从1开始算的!
android:layout_span="4":表示合并4个单元格,也就说这个组件占4个单元格
RelativeLayout(相对布局)
LinearLayout(线性布局)
listview作为一个列表控件,他和普通的列表一样,可以自己设置表头与表尾: 以及分割线,可供我们设置的属性如下:
· footerDividersEnabled:是否在footerView(表尾)前绘制一个分隔条,默认为true
· headerDividersEnabled:是否在headerView(表尾)前绘制一个分隔条,默认为true
· divider:设置分隔条,可以用颜色分割,也可以用drawable资源分割
· dividerHeight:设置分隔条的高度
翻遍了了API发现并没有可以直接设置ListView表头或者表尾的属性,只能在Java中写代码 进行设置了,可供我们调用的方法如下:
· addHeaderView(View v):添加headView(表头),括号中的参数是一个View对象
· addFooterView(View v):添加footerView(表尾),括号中的参数是一个View对象
· addHeaderView(headView, null, false):和前面的区别:设置Header是否可以被选中
· addFooterView(View,view,false):同上
对了,使用这个addHeaderView方法必须放在listview.setAdapter前面,否则会报错。
4.设置点击颜色cacheColorHint
如果你为ListView设置了一个图片作为Background的话,当你拖动或者点击listView空白位置会发现 item都变成黑色了,这是时候我们可以通过这个cacheColorHint将颜色设置为透明:#00000000
5.隐藏滑动条
我们可以通过设置:android:scrollbars="none" 或者setVerticalScrollBarEnabled(true); 解决这个问题!
3.列表从底部开始显示:stackFromBottom
如果你想让列表显示你列表的最下面的话,那么你可以使用这个属性,将stackFromBottom 属性设置为true即可,设置后的效果图如下:
ViewHolder重用组件
嘿嘿,getView()会被调用多次,那么findViewById不一样得调用多次,而我们的ListView的Item 一般都是一样的布局,我们可以对这里在优化下,我们可以自己定义一个ViewHolder类来对这一部分 进行性能优化!修改后的代码如下:
@Override
public View getView(intposition, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if(convertView == null){
convertView =LayoutInflater.from(mContext).inflate(R.layout.item_list_animal,parent,false);
holder = new ViewHolder();
holder.img_icon = (ImageView) convertView.findViewById(R.id.img_icon);
holder.txt_aName = (TextView)convertView.findViewById(R.id.txt_aName);
holder.txt_aSpeak = (TextView)convertView.findViewById(R.id.txt_aSpeak);
convertView.setTag(holder); //将Holder存储到convertView中
}else{
holder = (ViewHolder)convertView.getTag();
}
holder.img_icon.setBackgroundResource(mData.get(position).getaIcon());
holder.txt_aName.setText(mData.get(position).getaName());
holder.txt_aSpeak.setText(mData.get(position).getaSpeak());
return convertView;
}
static class ViewHolder{
ImageView img_icon;
TextView txt_aName;
TextView txt_aSpeak;
}
没错就是这么简单,你以后BaseAdapter照着这个模板写就对了,哈哈,另外这个修饰ViewHolder的 static,关于是否定义成静态,跟里面的对象数目是没有关系的,加静态是为了在多个地方使用这个 Holder的时候,类只需加载一次,如果只是使用了一次,加不加也没所谓!——Berial(B神)原话~
WebView文件下载
1.调用其它浏览器下载文件:
这个很简单,我们只需为WebView设置setDownloadListener,然后重写DownloadListener的 onDownloadStart,然后在里面写个Intent,然后startActivity对应的Activity即可!
关键代码如下:
wView.setDownloadListener(new DownloadListener(){
@Override
public voidonDownloadStart(String url, String userAgent, String contentDisposition,
String mimetype, long contentLength) {
Log.e("HEHE","开始下载");
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW,uri);
startActivity(intent);
}
});
2.自己写线程下载文件
当然,你可能不想把下载文件放到默认路径下,或者想自己定义文件名等等,你都可以自己来写 一个线程来下载文件,实现示例代码如下:
核心代码:
我们自己另外写一个下载的线程类:
DownLoadThread.java
/**
* Created by Jay on 2015/9/14 0014.
*/
public class DownLoadThread implements Runnable {
private String dlUrl;
public DownLoadThread(String dlUrl) {
this.dlUrl = dlUrl;
}
@Override
public void run() {
Log.e("HEHE", "开始下载~~~~~");
InputStream in = null;
FileOutputStream fout = null;
try {
URL httpUrl = new URL(dlUrl);
HttpURLConnection conn = (HttpURLConnection) httpUrl.openConnection();
conn.setDoInput(true);
conn.setDoOutput(true);
in = conn.getInputStream();
File downloadFile, sdFile;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
Log.e("HEHE","SD卡可写");
downloadFile = Environment.getExternalStorageDirectory();
sdFile = new File(downloadFile, "csdn_client.apk");
fout = new FileOutputStream(sdFile);
}else{
Log.e("HEHE","SD卡不存在或者不可读写");
}
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
fout.write(buffer, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fout != null) {
try {
fout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Log.e("HEHE", "下载完毕~~~~");
}
}
然后MainActivity.java中创建并启动该线程:
wView.setDownloadListener(new DownloadListener(){
@Override
public void onDownloadStart(String url, String userAgent, String contentDisposition,
String mimetype, long contentLength) {
Log.e("HEHE","onDownloadStart被调用:下载链接:" + url);
new Thread(new DownLoadThread(url)).start();
}
});
Java中定义ColorDrawable:
ColorDrawable drawable = new ColorDrawable(0xffff2200);
txtShow.setBackground(drawable);
利用静态方法argb来设置颜色:
Android使用一个int类型的数据表示颜色值,通常是十六进制,即0x开头, 颜色值的定义是由透明度alpha和RGB(红绿蓝)三原色来定义的,以"#"开始,后面依次为:
透明度-红-绿-蓝;eg:#RGB#ARGB #RRGGBB #AARRGGBB
每个要素都由一个字节(8bit)来表示,所以取值范围为0~255,在xml中设置颜色可以忽略透明度, 但是如果你是在Java代码中的话就需要明确指出透明度的值了,省略的话表示完全透明,这个时候 就没有效果了哦~比如:0xFF0000虽然表示红色,但是如果直接这样写,什么的没有,而应该这样写: 0xFFFF0000,记Java代码设置颜色值,需要在前面添加上透明度~ 示例:(参数依次为:透明度,红色值,绿色值,蓝色值) txtShow.setBackgroundColor(Color.argb(0xff,0x00, 0x00, 0x00));
ShapeDrawable
形状的Drawable咯,定义基本的几何图形,如(矩形,圆形,线条等),根元素是<shape../> 节点比较多,相关的节点如下:
· ① <shape>:
· ~ visible:设置是否可见
· ~ shape:形状,可选:rectangle(矩形,包括正方形),oval(椭圆,包括圆),line(线段),ring(环形)
· ~ innerRadiusRatio:当shape为ring才有效,表示环内半径所占半径的比率,如果设置了innerRadius, 他会被忽略
· ~ innerRadius:当shape为ring才有效,表示环的内半径的尺寸
· ~ thicknessRatio:当shape为ring才有效,表环厚度占半径的比率
· ~ thickness:当shape为ring才有效,表示环的厚度,即外半径与内半径的差
· ~ useLevel:当shape为ring才有效,表示是否允许根据level来显示环的一部分
· ②<size>:
· ~ width:图形形状宽度
· ~ height:图形形状高度
· ③<gradient>:后面GradientDrawable再讲~
· ④<solid>
· ~ color:背景填充色,设置solid后会覆盖gradient设置的所有效果!!!!!!
· ⑤<stroke>
· ~ width:边框的宽度
· ~ color:边框的颜色
· ~ dashWidth:边框虚线段的长度
· ~ dashGap:边框的虚线段的间距
· ⑥<conner>
· ~ radius:圆角半径,适用于上下左右四个角
· ~ topLeftRadius,topRightRadius,BottomLeftRadius,tBottomRightRadius:依次是左上,右上,左下,右下的圆角值,按自己需要设置!
· ⑦<padding>
· left,top,right,bottm:依次是左上右下方向上的边距!
GradientDrawable
一个具有渐变区域的Drawable,可以实现线性渐变,发散渐变和平铺渐变效果 核心节点:<gradient/>,有如下可选属性:
· startColor:渐变的起始颜色
· centerColor:渐变的中间颜色
· endColor:渐变的结束颜色
· type:渐变类型,可选(linear,radial,sweep),线性渐变(可设置渐变角度),发散渐变(中间向四周发散),平铺渐变
· centerX:渐变中间亚瑟的x坐标,取值范围为:0~1
· centerY:渐变中间颜色的Y坐标,取值范围为:0~1
· angle:只有linear类型的渐变才有效,表示渐变角度,必须为45的倍数哦
· gradientRadius:只有radial和sweep类型的渐变才有效,radial必须设置,表示渐变效果的半径
· useLevel:判断是否根据level绘制渐变效果
· 先在drawable下创建三个渐变xml文件:
· (线性渐变)gradient_linear.xml:
· <?xml version="1.0" encoding="utf-8"?>
· <shape
· xmlns:android="http://schemas.android.com/apk/res/android"
· android:shape="oval" >
· <gradient
· android:angle="90"
· android:centerColor="#FFEB82"
· android:endColor="#35B2DE"
· android:startColor="#DEACAB" />
·
· <stroke
· android:dashGap="5dip"
· android:dashWidth="4dip"
· android:width="3dip"
· android:color="#fff" />
· </shape>
· (发散渐变)gradient_radial.xml:
· <?xml version="1.0" encoding="utf-8"?>
· <shape xmlns:android="http://schemas.android.com/apk/res/android"
· android:innerRadius="0dip"
· android:shape="ring"
· android:thickness="70dip"
· android:useLevel="false" >
·
· <gradient
· android:centerColor="#FFEB82"
· android:endColor="#35B2DE"
· android:gradientRadius="70"
· android:startColor="#DEACAB"
· android:type="radial"
· android:useLevel="false" />
·
· </shape>
· (平铺渐变)gradient_sweep.xml:
· <?xml version="1.0" encoding="utf-8"?>
· <shape xmlns:android="http://schemas.android.com/apk/res/android"
· android:innerRadiusRatio="8"
· android:shape="ring"
· android:thicknessRatio="3"
· android:useLevel="false" >
·
· <gradient
· android:centerColor="#FFEB82"
· android:endColor="#35B2DE"
· android:startColor="#DEACAB"
· android:type="sweep"
· android:useLevel="false" />
·
· </shape>
InsetDrawable
表示把一个Drawable嵌入到另外一个Drawable的内部,并且在内部留一些间距, 类似与Drawable的padding属性,但padding表示的是Drawable的内容与Drawable本身的边距! 而InsetDrawable表示的是两个Drawable与容器之间的边距,当控件需要的背景比实际的边框 小的时候,比较适合使用InsetDrawable,比如使用这个可以解决我们自定义Dialog与屏幕之间 的一个间距问题,相信做过的朋友都知道,即使我们设置了layout_margin的话也是没用的,这个 时候就可以用到这个InsetDrawable了!只需为InsetDrawable设置一个insetXxx设置不同 方向的边距,然后为设置为Dialog的背景即可!
相关属性如下:
· 1.drawable:引用的Drawable,如果为空,必须有一个Drawable类型的子节点!
· 2.visible:设置Drawable是否额空间
· 3.insetLeft,insetRight,insetTop,insetBottm:设置左右上下的边距
①XML中使用:
<?xmlversion="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/test1"
android:insetBottom="10dp"
android:insetLeft="10dp"
android:insetRight="10dp"
android:insetTop="10dp" />
在Java代码中使用:
InsetDrawable insetDrawable =new InsetDrawable(getResources()
.getDrawable(R.drawable.test1), 10, 10,10, 10);
ClipDrawable
Clip可以译为剪的意思,我们可以把ClipDrawable理解为从位图上剪下一个部分; Android中的进度条就是使用ClipDrawable来实现的,他根据设置level的值来决定剪切 区域的大小,根节点是<clip>
相关属性如下:
· clipOrietntion:设置剪切的方向,可以设置水平和竖直2个方向
· gravity:从那个位置开始裁剪
· drawable:引用的drawable资源,为空的话需要有一个Drawable类型的子节点 ps:这个Drawable类型的子节点:就是在<clip里>加上这样的语句: 这样...
使用示例:
核心:通过代码修改ClipDrawable的level的值!Level的值是0~10000!
运行效果图:
代码实现:
①定义一个ClipDrawable的资源xml:
<?xmlversion="1.0" encoding="utf-8"?>
<clipxmlns:android="http://schemas.android.com/apk/res/android"
android:clipOrientation="horizontal"
android:drawable="@mipmap/ic_bg_meizi"
android:gravity="left" />
②在activity_main主布局文件中设置一个ImageView,将src设置为clipDrawable! 记住是src哦,如果你写成了blackground的话可是会报空指针的哦!!!!
<LinearLayoutxmlns: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"
android:orientation="vertical">
<ImageView
android:id="@+id/img_show"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/clip_bg" />
</LinearLayout>
③MainActivity.java通过setLevel设置截取区域大小:
public class MainActivityextends AppCompatActivity {
private ImageView img_show;
private ClipDrawable cd;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg){
if (msg.what == 0x123) {
cd.setLevel(cd.getLevel() +500);
}
}
};
@Override
protected void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
img_show = (ImageView)findViewById(R.id.img_show);
// 核心实现代码
cd = (ClipDrawable)img_show.getDrawable();
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
handler.sendEmptyMessage(0x123);
if (cd.getLevel() >= 10000){
timer.cancel();
}
}
}, 0, 300);
}
}
获取Bitmap位图
从资源中获取位图的方式有两种:通过BitmapDrawable或者BitmapFactory,下面演示下: 我们首先得获得这个
BitmapDrawable方法:
你可以创建一个构造一个BitmapDrawable对象,比如通过流构建BitmapDrawable:
BitmapDrawable bmpMeizi = newBitmapDrawable(getAssets().open("pic_meizi.jpg"));
Bitmap mBitmap =bmpMeizi.getBitmap();
img_bg.setImageBitmap(mBitmap);
BitmapFactory方法:
都是静态方法,直接调,可以通过资源ID、路径、文件、数据流等方式来获取位图!
//通过资源ID
private BitmapgetBitmapFromResource(Resources res, int resId) {
return BitmapFactory.decodeResource(res,resId);
}
//文件
private BitmapgetBitmapFromFile(String pathName) {
return BitmapFactory.decodeFile(pathName);
}
//字节数组
public BitmapBytes2Bimap(byte[] b) {
if (b.length != 0) {
return BitmapFactory.decodeByteArray(b,0, b.length);
} else {
return null;
}
}
//输入流
private BitmapgetBitmapFromStream(InputStream inputStream) {
returnBitmapFactory.decodeStream(inputStream);
}
获取Bitmap的相关信息:
这个,只要我们获取了Bitmap对象,就可以调用相关方法来获取对应的参数了,getByteCount获得大小, getHeight和getWidth这些~这里就不写了,自己查文档!
抠图片上的某一角下来
有时,可能你想把图片上的某一角扣下来,直接通过Bitmap的createBitmap()扣下来即可 参数依次为:处理的bitmap对象,起始x,y坐标,以及截取的宽高
Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.mipmap.pic_meizi);
Bitmap bitmap2 = Bitmap.createBitmap(bitmap1,100,100,200,200);
img_bg = (ImageView) findViewById(R.id.img_bg);
img_bg.setImageBitmap(bitmap2);
对Bitmap进行缩放
我们这里不用Matrix来对Bitmap,而是直接使用Bitmap给我们提供的createScaledBitmap来实现, 参数依次是:处理的bitmap对象,缩放后的宽高,
BlurMaskFilter(模糊效果)
BlurMaskFilter(10f,BlurMaskFilter.Blur.NORMAL);
我们可以控制的就是这两个参数:
第一个参数:指定模糊边缘的半径;
第二个参数:指定模糊的风格,可选值有:
- BlurMaskFilter.Blur.NORMAL:内外模糊
- BlurMaskFilter.Blur.OUTER:外部模糊
- BlurMaskFilter.Blur.INNER:内部模糊
- BlurMaskFilter.Blur.SOLID:内部加粗,外部模糊
EmbossMaskFilter(浮雕效果)
EmbossMaskFilter(float[]direction, float ambient, float specular, float blurRadius) 参数依次是:
direction:浮点型数组,用于控制x,y,z轴的光源方向
ambient:设置环境光亮度,0到1之间
specular:镜面反射系数
blurRadius:模糊半径
注意事项
在使用MaskFilter的时候要注意,当我们的targetSdkVersion >=14的时候,MaskFilter 就不会起效果了,这是因为Android在API 14以上版本都是默认开启硬件加速的,这样充分 利用GPU的特性,使得绘画更加平滑,但是会多消耗一些内存!好吧,我们把硬件加速关了就好,可以在不同级别下打开或者关闭硬件加速,一般是关闭~
· Application:在配置文件的application节点添加:android:hardwareAccelerated="true"
· Activity:在配置文件的activity节点添加android:hardwareAccelerated="false"
· View:可以获得View对象后调用,或者直接在View的onDraw()方法里设置:view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
AvoidXfermode
嗯,和前面学的MaskFilter的两个子类一样,不支持硬件加速,所以如果是API 14以上的版本, 需要关闭硬件加速才会有效果!怎么关自己看上一节哈~
PS:需要在AndroidManifest.xml中的appliction节点添加关闭硬件加速: android:hardwareAccelerated="false"
我们来看看他给我们提供的构造方法!官方API文档:AvoidXfermode
参数有三个,依次是:
opColor:一个十六进制的带透明度的颜色值,比如0x00C4C4;
tolerance:容差值,如果你学过PS可能用过魔棒工具,就是设置选取颜色值的范围,比如 容差为0,你选的是纯黑的小点,当容差调为40的时候,范围已经扩大到大块黑色这样!如果 还不是很明白,等下我们写写代码就知道了!
mode:AvoidXfermode模式,有两种:TARGET与AVOID
模式1:AvoidXfermode.Mode.TARGET
该模式会判断画布上是否有与我们设置颜色值不一样的颜色,如果有的话,会把这些区域 染上一层画笔定义的颜色,其他地方不染色!下面我们写代码演示下,顺便让大家感觉下这个容差值!
模式2:AvoidXfermode.Mode.AVOID
和上面的TARGET模式相反,上面是颜色一样才改变颜色,这里是颜色不一样反而改变颜色, 而容差值同样带来相反的结果,容差值为0时,只有当图片中的像素颜色值与设置的颜色值完全不一样 的时候才会被染色,而当容差值达到最大值255的时候,稍微有一点颜色不一样就会被染色! 我们只需简单的修改上面的例子就可以了,同一是修改下构造AvoidXfermode的内容! 我们改成下面这句:
avoidXfermode = new AvoidXfermode(0XFFD9E5F3,230, AvoidXfermode.Mode.AVOID);
构造方法详解
1)BitmapShader(图像渲染)
BitmapShader(Bitmapbitmap, Shader.TileMode tileX, Shader.TileMode tileY)
使用一张位图作为纹理来对某一区域进行填充,参数依次:
- bitmap:用来作为填充的位图;
- tileX:X轴方向上位图的衔接形式;
- tileY:Y轴方向上位图的衔接形式;
而这个Shader.TileMode有三种:
- CLAMP就是如果渲染器超出原始边界范围,则会复制边缘颜色对超出范围的区域进行着色
- REPEAT则是平铺形式重复渲染
- MIRROR则是在横向和纵向上以镜像的方式重复渲染位图。
2)ComposeShader(混合渲染)
ComposeShader(ShadershaderA, Shader shaderB, PorterDuff.Mode mode)
渲染效果的叠加,看到PorterDuff就知道什么了吧?比如将BitmapShader与LinearGradient的混合渲染 效果等。参数依次:
- shaderA:第一种渲染效果
- shaderB:第二种渲染效果
- mode:两种渲染效果的叠加模式
3)LinearGradient(线性渲染)
LinearGradient(floatx0, float y0, float x1, float y1, int[] colors, float[] positions,Shader.TileMode tile);
实现某一区域内颜色的线性渐变效果,参数依次是:
- x0:渐变的起始点x坐标
- y0:渐变的起始点y坐标
- x1:渐变的终点x坐标
- y1:渐变的终点y坐标
- colors:渐变的颜色数组
- positions:颜色数组的相对位置
- tile:平铺方式
4)RadialGradient(环形渲染)
publicRadialGradient (float x, float y, float radius, int[] colors, float[]positions, Shader.TileMode tile);
实现某一区域内颜色的环形渐变效果,参数依次是:
- x:环形的圆心x坐标
- y:环形的圆心y坐标
- radius:环形的半径
- colors:环形渐变的颜色数组
- positions:指定颜色数组的相对位置
- tile:平铺方式
5)SweepGradient(梯度渲染)
publicSweepGradient (float cx, float cy, int[] colors, float[] positions)
扫描渲染,就是以某个点位中心旋转一周所形成的效果!参数依次是:
- cx:扫描的中心x坐标
- cy:扫描的中心y坐标
- colors:梯度渐变的颜色数组
- positions:指定颜色数组的相对位置
Matrix中的几个常用的变换方法
· setTranslate(float dx, float dy):控制Matrix进行平移
· setRotate(float degrees, float px, float py):旋转,参数依次是:旋转角度,轴心(x,y)
· setScale(float sx, float sy, float px, float py):缩放, 参数依次是:X,Y轴上的缩放比例;缩放的轴心
· setSkew(float kx, float ky):倾斜(扭曲),参数依次是:X,Y轴上的缩放比例
translate(平移)
方法:translate(float dx, floatdy)
解析:平移,将画布的坐标原点向左右方向移动x,向上下方向移动y,canvas默认位置在(0,0)
参数:dx为水平方向的移动距离,dy为垂直方向的移动距离
使用示例:
for(int i=0; i < 5; i++) {
canvas.drawCircle(50, 50, 50, mPaint);
canvas.translate(100, 100);
}
rotate(旋转)
方法:rotate(float degrees) / rotate(floatdegrees, float px, float py)
解析:围绕坐标原点旋转degrees度,值为正顺时针
参数:degrees为旋转角度,px和py为指定旋转的中心点坐标(px,py)
使用示例:
Rect rect = new Rect(50,0,150,50);
canvas.translate(200, 200);
for(int i = 0; i < 36;i++){
canvas.rotate(10);
canvas.drawRect(rect, mPaint);
}
scale(缩放)
方法:scale(float sx, float sy)/ scale(float sx, float sy, float px, float py)
解析:对画布进行缩放
参数:sx为水平方向缩放比例,sy为竖直方向的缩放比例,px和py我也不知道,小数为缩小, 整数为放大
使用示例:
canvas.drawBitmap(bmp,0,0,mPaint);
canvas.scale(0.8f, 0.8f);
canvas.drawBitmap(bmp, 0, 0, mPaint);
canvas.scale(0.8f, 0.8f);
canvas.drawBitmap(bmp,0,0,mPaint);
skew(倾斜)
方法:skew(float sx, float sy)
解析:倾斜,也可以译作斜切,扭曲
参数:sx为x轴方向上倾斜的对应角度,sy为y轴方向上倾斜的对应角度,两个值都是tan值哦! 都是tan值!都是tan值!比如要在x轴方向上倾斜60度,那么小数值对应:tan60 = 根号3 = 1.732!
使用示例:
canvas.drawBitmap(bmp,0,0,mPaint);
canvas.translate(200, 200);
canvas.skew(0.2f,-0.8f);
canvas.drawBitmap(bmp,0,0,mPaint);
Canvas图层的概念以及save()和restore()详解
我们一般喜欢称呼Canvas为画布,童鞋们一直觉得Canvas就是一张简单的画纸,那么我想问下多层的动画是怎么用canvas来完成的?上面那个translate平移的例子,为什么 drawCircle(50, 50, 50, mPaint); 参考坐标一直是(50,50)那为何会出现这样的效果?有疑惑的童鞋可能是一直将屏幕的概念与Canvas的概念混淆了,下面我们来还原下 调用translate的案发现场:
如图,是画布坐标原点的每次分别在x,y轴上移动100;那么假如我们要重新回到(0,0) 点处绘制新的图形呢?怎么破,translate(-100,-100)的慢慢地平移回去?不会真的这么纠结吧...
好吧,不卖关子了,我们可以在做平移变换之前将当前canvas的状态进行保存,其实Canvas为 我们提供了图层(Layer)的支持,而这些Layer(图层)是按"栈结构"来进行管理的
当我们调用save()方法,会保存当前Canvas的状态然后作为一个Layer(图层),添加到Canvas栈中,另外,这个Layer(图层)不是一个具体的类,就是一个概念性的东西而已!
而当我们调用restore()方法的时候,会恢复之前Canvas的状态,而此时Canvas的图层栈 会弹出栈顶的那个Layer,后继的Layer来到栈顶,此时的Canvas回复到此栈顶时保存的Canvas状态!
简单说就是:save()往栈压入一个Layer,restore()弹出栈顶的一个Layer,这个Layer代表Canvas的状态!也就是说可以save()多次,也可以restore()多次,但是restore的调用次数不能大于save 否则会引发错误!这是网上大部分的说法,不过实际测试中并没有出现这样的问题,即使我restore的 次数多于save,也没有出现错误~目测是系统改了,等下测给大家看~ 来来来,写个例子验证下save和restore的作用!
写个例子:
例子代码:
canvas.save(); //保存当前canvas的状态
canvas.translate(100, 100);
canvas.drawCircle(50, 50, 50, mPaint);
canvas.restore(); //恢复保存的Canvas的状态
canvas.drawCircle(50, 50, 50, mPaint);