ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Android APP完整基础教程(16)图形系统-图像特效

2022-02-06 11:02:01  阅读:219  来源: 互联网

标签:图形系统 16 APP private paint shaders offset path new


该部分主要基于Canvas的绘图系统介绍一些常见的特效。

1 扭曲特效

这里使用Canvas的drawBitmapmesh对图像的局部进行扭曲特效处理。该效果主要是在APP上显示“水波荡漾”、“红旗飘扬”等扭曲效果,非常灵活。drawBitmapmesh定义如下:

public void drawBitmapMesh (Bitmap bitmap, 
                int meshWidth,int meshHeight, 
                float[] verts,int vertOffset, 
                int[] colors,int colorOffset,Paint paint)

主要有几个关键参数(其他参数相对好理解一些):

  • meshWidth 和 meshHeight:在横向/纵向把图片切分成多少个格子。
  • verts:该参数是一个长度len为(meshWidth+1)*(meshHeight+1)*2的数组,记录了扭曲后位图的各个顶点,记录格式为:(x1,y1)、(x2,y2)、(x3,y3)、。。。该方法主要是通过这些数组元素来控制对bitmap位图的扭曲效果(在下面实战demo中会进一步了解)。

drawBitmapmesh的实际扭曲效果图如下所示:

关于drawBitmapmesh方法更多解读可参照文档: Android Canvas drawBitmapMesh详解

@2 drawBitmapmesh实战(drawBitmapmesh效果展示)

实现功能:触摸屏幕,在触摸处展示drawBitmapmesh的扭曲效果。程序运行效果如下所示:

关于该程序,自定义View的关键代码如下所示:

public class MyView extends View {
    private static final String TAG = "MyView";
    private Matrix initMatrix = new Matrix();
    private int h,w;
    //为drawBitmapMesh构建的相关变量,start
    private final static int WIDTH =20;
    private final static int HEIGHT =20;
    private final static int COUNT = (WIDTH+1)*(HEIGHT+1);
    private float[] verts = new float[COUNT*2];
    private float[] origs = new float[COUNT*2];
    //为drawBitmapMesh构建的相关变量,end
    public Bitmap bitmap = null;
    float offsetStartX=0.0f,offsetStartY=0.0f;//居中显示调整

    public MyView(Context context) {
        super(context);
        init(context);
    }

    private void init(Context context){
        //1 获取bitmap
        initMatrix.reset();
        initMatrix.setScale(1.0f,1.0f);
        try {
            InputStream isImage = context.getAssets().open("test6.png");
            bitmap = BitmapFactory.decodeStream(isImage);
            w = bitmap.getWidth();
            h = bitmap.getHeight();
            bitmap = Bitmap.createBitmap(bitmap,0,0, w, h, initMatrix, true);
        } catch (IOException e) {
            e.printStackTrace();
        }

        //2 按照drawBitmapMesh规定方式构建origs数组(处理数据)和verts数组(保存原始数据)
        int index=0;
        for(int i=0;i<HEIGHT+1;i++){
            float fy=(h*i)/HEIGHT;
            for(int j=0;j<WIDTH+1;j++){
                float fx=(w*j)/WIDTH;
                //记录格式为:(x1,y1)、(x2,y2)、(x3,y3),以此类推
                origs[index*2+0]=verts[index*2+0]=fx;
                origs[index*2+1]=verts[index*2+1]=fy;
                index++;
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.WHITE);
        //居中显示
        offsetStartX = (canvas.getWidth()-bitmap.getWidth())*1.0f/2;
        offsetStartY = (canvas.getHeight()-bitmap.getHeight())*1.0f/2;
        canvas.translate(offsetStartX,offsetStartY);
        //5 bitmap绘制,当vert数组变化时该图片中局部会做相应的调整。
        canvas.drawBitmapMesh(bitmap,WIDTH,HEIGHT,verts,0,null,0,null);
    }

    private void wrap(float x,float y){
        //4 关键算法,扭曲效果设计,根据输入的事件坐标构建扭曲效果。
        for(int i=0;i<COUNT*2;i+=2){
            float dx = x-origs[i];
            float dy = y-origs[i+1];
            float d = (float)Math.sqrt(dx*dx+dy*dy);
            float pull = 200000.f/(d*d*d);
            if(pull>=1){
                verts[i] = x;
                verts[i+1] = y;
            }else{
                verts[i] = origs[i]+dx*pull;
                verts[i+1] = origs[i+1]+dy*pull;
            }
        }
        invalidate();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //3 为配合居中显示,这里做了坐标校正。
        wrap(event.getX()-offsetStartX,event.getY()-offsetStartY);
        return true;
    }
}

在MainActivity中实现代码为:

public class MainActivity extends AppCompatActivity {
    private static String TAG = "MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new MyView(this));
    }
}

2 shader特效

@1 shader概念基础与派生类解读

这里主要是使用Paint画笔的填充模式展示shader的填充效果。在Android APP中,Shader类(更多关于Shader类解读查看文档:Android Shader类详解)是一个抽象类,它的派生类如下:

shader是着色器的含义(就像皮肤一样,只不过我们看到的一个人穿的衣服还要依赖于环境,比如光照、阴影等,也就是说shader可以理解为 衣服在一个人身上 整体呈现的最终效果),更多关于shader的解读可查看文章:OpenGL基础(01)基础知识中的第二部分即可。

@2 shader效果实战(展示5种shader派生类特效)

实现功能:通过按键响应5种shader效果,效果如下所示:

关于该程序,自定义View的关键代码如下所示:

public class MyView extends View {
    private static final String TAG = "MyView";
    public Paint paint = new Paint();
    private Path path = new Path();

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

    private void init(){
        paint.setAntiAlias(true);//抗锯齿
        paint.setColor(Color.RED);//画笔颜色
        paint.setStyle(Paint.Style.FILL);//填充模式
        paint.setStrokeWidth(4f);//设置画笔粗细度
        paint.setTextSize(48f);
    }

    public void setShader(Shader shader){
        paint.setShader(shader);
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.WHITE);
        drawsth(canvas,paint,path,440f);
    }
	
    //测试:绘制基本图形、Path不规则图形和文字
    private void drawsth(Canvas canvas, Paint paint,Path path,float offset){

        //paint绘制圆形,参数:圆心坐标(x,y)半径r,画笔paint
        canvas.drawCircle(100f+offset,100f,90f,paint);
        //paint绘制矩形,参数:左上角坐标(x,y),右下角坐标(x,y),画笔paint
        canvas.drawRect(10f+offset,200f,210f+offset,400f,paint);
        //paint绘制圆角矩形,参数:左上角坐标(x,y),右下角坐标(x,y),x方向上的圆角半径,y方向上的圆角半径,画笔paint
        canvas.drawRoundRect(10f+offset,410f,210f+offset,610f,40f,40f,paint);
        //paint绘制椭圆,参数:左上角坐标(x,y),右下角坐标(x,y)[参数表示矩形内切的椭圆],画笔paint
        canvas.drawOval(10f+offset,620f,210f+offset,720f,paint);

        //Path绘制三角形,moveto表示第1个坐标,lineto分别表示第2、3个坐标。以此类推。
        path.moveTo(110f+offset,730f);
        path.lineTo(10f+offset,930f);
        path.lineTo(210f+offset,930f);
        path.close();
        canvas.drawPath(path,paint);
        //Path绘制不规则多点多边形
        path.moveTo(10f+offset,950f);
        path.lineTo(50f+offset,1000f);
        path.lineTo(200f+offset,970f);
        path.lineTo(150f+offset,1070f);
        path.lineTo(10f+offset,1110f);
        path.lineTo(210f+offset,1130f);
        path.close();
        canvas.drawPath(path,paint);

        //注意:这个坐标(x,y)并不是文字的左上角,而是一个与左下角比较接近的位置。
        canvas.drawText("测试文字",10f+offset,1190f,paint);
    }
}

在MainActivity中实现代码如下所示:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static String TAG = "MainActivity";
    private Shader[] shaders = new Shader[5];
    private int[] colors = new int[]{Color.RED,Color.GREEN,Color.BLUE};
    private MyView myView;
    private Button btn1,btn2,btn3,btn4,btn5;
    private Bitmap bitmap = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.shaderlayout);
        Button btn1 = findViewById(R.id.btn1);
        Button btn2 = findViewById(R.id.btn2);
        Button btn3 = findViewById(R.id.btn3);
        Button btn4 = findViewById(R.id.btn4);
        Button btn5 = findViewById(R.id.btn5);
        myView = findViewById(R.id.myView);

        try {
            InputStream isImage = getAssets().open("test6.png");
            bitmap = BitmapFactory.decodeStream(isImage);
        } catch (IOException e) {
            e.printStackTrace();
        }
		//创建5种shader的派生类对象,分别测试效果
        shaders[0] = new BitmapShader(bitmap,Shader.TileMode.REPEAT, Shader.TileMode.MIRROR);
        shaders[1] = new LinearGradient(0f,0f,100f,100f,colors,null, Shader.TileMode.REPEAT);
        shaders[2] = new RadialGradient(100f,100f,80f,colors,null, Shader.TileMode.REPEAT);
        shaders[3] = new SweepGradient(160f,160f,colors,null);
        shaders[4] = new ComposeShader(shaders[1],shaders[2], PorterDuff.Mode.ADD);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn1:myView.setShader(shaders[0]);break;
            case R.id.btn2:myView.setShader(shaders[1]);break;
            case R.id.btn3:myView.setShader(shaders[2]);break;
            case R.id.btn4:myView.setShader(shaders[3]);break;
            case R.id.btn5:myView.setShader(shaders[4]);break;
            default:break;
        }
    }
}

关于该程序,shaderlayout.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"
    android:orientation="vertical">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:visibility="visible">

        <Button
            android:id="@+id/btn1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="20"
            android:onClick="onClick"
            android:text="@string/BitmapShader" />

        <Button
            android:id="@+id/btn2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="20"
            android:onClick="onClick"
            android:text="@string/LinearGradient" />

        <Button
            android:id="@+id/btn3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="20"
            android:onClick="onClick"
            android:text="@string/RadialGradient" />

        <Button
            android:id="@+id/btn4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="20"
            android:onClick="onClick"
            android:text="@string/SweepGradient" />

        <Button
            android:id="@+id/btn5"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="20"
            android:onClick="onClick"
            android:text="@string/ComposeGradient" />
    </LinearLayout>

    <com.ags.myapplication.MyView
        android:id="@+id/myView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@mipmap/ic_launcher" />
</LinearLayout>

标签:图形系统,16,APP,private,paint,shaders,offset,path,new
来源: https://blog.csdn.net/vviccc/article/details/122791849

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有