android canvas_"给我一面国旗@微信官方" - Android-Canvas自己选择图片绘制拼接简单Play一下...

不多说,直接看效果先:

v2-237095a28c5aadb313c76149058ffaae_b.gif

小萌新简单耍一下,庆祝国庆放假。实现就是简单的Canvas的drawBitmap即可,主要逻辑就是处理下绘制位置。另外如果要保存导出,就需要将Canvas合成绘制的内容导出 - 小萌新做法是

1. 创建一个临时的Bitmap,

2. 然后需要绘制的两张图片都绘制到这个Bitmap上面,

3.最后将该Bitmap绘制到屏幕上,

4.最后如果想要到处,就直接导出改Bitmap就可以了。

相对麻烦的一点就是:国旗的位置(小萌新的是右下角,原始图片的宽度的1/4,高度则需要根据国旗图片高度来进行缩放计算)

v2-cb01aacbbd9d454d656b8528461b921a_b.jpg

有点要注意,小萌新显示原始图片的宽度最大就是屏幕宽度的一半(超过的则显示的时候以宽度一半为主),而两张图片合成到一张图片的时候,实际是往临时的Bitmap上绘制,所以绘制坐标就是临时Bitmap本身的范围来计算的,而不是显示范围,这个要区分下。

另外:其实完全可以直接再临时的Bitmap上面操作就可以的,还有就是代码的封装性这些要注意。。还有关于工具类的使用。我这里代码各方面不是很好,可能还有啥问题....自己需要可以简化完善下。。。

然后就可以简单撸下代码了:

GuoQiCanvas.java

package com.skl.testremoteservice;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;

/**
 * @HL - https://www.zhihu.com/people/monkey.d.luffy/activities
 * 简单图片拼接Play一下,@微信官方 给我一面国旗
 */
public class GuoQiCanvas extends View {
    private Context context;///< 上下文
    private Bitmap mBitmap, bitmapGuoQi;

    private Canvas cacheCanvas;
    private Bitmap cacheBitmap;
    private int screenWidth, screenHeight;

    private Rect src, dst;
    private Rect srcGuoqi, dstGuoqi, dstComposeSrc, dstComposeGuoqi;

    public GuoQiCanvas(Context context) {
        this(context, null);
    }

    public GuoQiCanvas(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public GuoQiCanvas(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public GuoQiCanvas(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        setLayerType(LAYER_TYPE_SOFTWARE, null);
        this.context = context;
        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        screenWidth = wm.getDefaultDisplay().getWidth();
        screenHeight = wm.getDefaultDisplay().getHeight();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //        if (null != mBitmap) {
        //            int originShowH = (mBitmap.getWidth() > screenWidth / 2 ? screenWidth / 2 : mBitmap.getWidth()) * mBitmap.getHeight() / mBitmap.getWidth();
        //            setMeasuredDimension(screenWidth, originShowH * 2);
        //        }
        // 直接全屏
        setMeasuredDimension(screenWidth, screenHeight);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // 合成图片 - 此时不绘制单个图片
        if (null != cacheBitmap) {
            cacheCanvas.drawBitmap(mBitmap, src, src, null);
            cacheCanvas.drawBitmap(bitmapGuoQi, srcGuoqi, dstComposeGuoqi, null);
            canvas.drawBitmap(cacheBitmap, src, dstComposeSrc, null);

            // 导出到相册
            exportToCamera();

            if (null != mBitmap) {
                mBitmap.recycle();
                mBitmap = null;
            }
            if (null != bitmapGuoQi) {
                bitmapGuoQi.recycle();
                bitmapGuoQi = null;
            }
        } else {
            // 绘制原始图片
            if (null != mBitmap) {
                canvas.drawBitmap(mBitmap, src, dst, null);
            }

            // 绘制拼接图片
            if (null != bitmapGuoQi) {
                canvas.drawBitmap(bitmapGuoQi, srcGuoqi, dstGuoqi, null);
            }
        }
    }

    /**
     * 绘制原始图片
     *
     * @param bitmap
     */
    public void drawSrcBitmap(Bitmap bitmap) {
        if (null != cacheBitmap) {
            cacheBitmap.recycle();
            cacheBitmap = null;
        }

        this.mBitmap = bitmap;

        src = new Rect(0, 0,
                mBitmap.getWidth(),
                mBitmap.getHeight());
        // 指定图片绘制区域(左上角的四分之一)
        int originShowW = (mBitmap.getWidth() > screenWidth / 2 ? screenWidth / 2 : mBitmap.getWidth());
        int originShowH = originShowW * mBitmap.getHeight() / mBitmap.getWidth();
        dst = new Rect(0, 0,
                originShowW,
                originShowH);

        // 根据图片信息更新控件大小 - 还是保持全屏吧
        // requestLayout();
        // 请求刷新渲染
        invalidate();
    }

    /**
     * 绘制拼接的目标图片
     *
     * @param bitmap
     */
    public void drawDstBitmap(Bitmap bitmap) {
        if (null != cacheBitmap) {
            cacheBitmap.recycle();
            cacheBitmap = null;
        }

        this.bitmapGuoQi = bitmap;

        srcGuoqi = new Rect(0, 0,
                bitmapGuoQi.getWidth(),
                bitmapGuoQi.getHeight());
        // 指定图片绘制区域(右上角的四分之一)
        int originShowW = (bitmapGuoQi.getWidth() > screenWidth / 2 ? screenWidth / 2 : bitmapGuoQi.getWidth());
        int originShowH = originShowW * bitmapGuoQi.getHeight() / bitmapGuoQi.getWidth();
        dstGuoqi = new Rect(
                screenWidth / 2,
                0,
                originShowW + screenWidth / 2,
                originShowH);

        invalidate();
    }

    /**
     * 图像合成
     */
    public boolean compose() {
        // 有了就导出到相册
        if (null != cacheBitmap){
            exportToCamera();
            return true;
        }

        if (null == mBitmap) {
            return false;
        }

        if (null == bitmapGuoQi) {
            return false;
        }

        this.cacheBitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        this.cacheCanvas = new Canvas(cacheBitmap);

        // 国旗图片位置
        int guoqiShowW = mBitmap.getWidth() * 3 / 4;
        int guoqiShowH = mBitmap.getWidth() * 1 / 4 * bitmapGuoQi.getHeight() / bitmapGuoQi.getWidth();
        this.dstComposeGuoqi = new Rect(
                guoqiShowW,
                mBitmap.getHeight() - guoqiShowH,
                mBitmap.getWidth(),
                mBitmap.getHeight());

        // 合成图片cacheBitmap绘制的位置
        this.dstComposeSrc = new Rect(
                dst.width() / 2,
                0,
                dst.width() / 2 + dst.width(),
                dst.height());

        invalidate();
        return true;
    }

    /**
     * 导出到相册
     */
    public void exportToCamera() {
        if (null != cacheBitmap) {
            File sdRoot = context.getCacheDir();
            String save_path = sdRoot + "/temp.png";

            File file = new File(save_path);
            if (file.exists()) {
                file.delete();
            }
            try {
                FileOutputStream fileOutputStream = new FileOutputStream(file);
                cacheBitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
                fileOutputStream.close();

                // 插入到相册
                saveBmp2Gallery(cacheBitmap, save_path);
                Toast.makeText(context, "已经保存到相册", Toast.LENGTH_SHORT).show();
            } catch (Exception e) {
                Log.e("GuoQi Error--------->", e.toString());
            }
        }
    }

    /**
     * @param bmp 获取的bitmap数据
     * @param pic 图片路径
     */
    public void saveBmp2Gallery(Bitmap bmp, String pic) {
        // 声明文件对象
        File file = new File(pic);

        // 插入相册
        MediaStore.Images.Media.insertImage(context.getContentResolver(),
                bmp, "GuoQi", null);
        // 通知相册更新
        Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        Uri uri = Uri.fromFile(file);
        intent.setData(uri);
        context.sendBroadcast(intent);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        Log.e("GuoQi", "onDetachedFromWindow");
        if (null != cacheBitmap) {
            cacheBitmap.recycle();
            cacheBitmap = null;
        }
    }
}

布局 activity_main.xml

   <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.skl.testremoteservice.GuoQiCanvas
        android:id="@+id/aiguoCanvas"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/colorPrimaryDark"
        app:layout_constraintTop_toTopOf="parent" />

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent">

        <Button
            android:id="@+id/originBtn"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:onClick="PicOpt"
            android:text="选择头像"
            android:textColor="@color/colorAccent"
            android:textSize="22sp"
            app:layout_constraintEnd_toStartOf="@+id/targetBtn"
            app:layout_constraintHorizontal_weight="1"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:id="@+id/targetBtn"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:onClick="PicOpt"
            android:text="选择贴图"
            android:textColor="@color/colorAccent"
            android:textSize="22sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_weight="1"
            app:layout_constraintStart_toEndOf="@+id/originBtn"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:id="@+id/composeBtn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="PicOpt"
            android:text="合成/导出到相册"
            android:textColor="@color/colorPrimary"
            android:textSize="22sp"
            app:layout_constraintTop_toBottomOf="@+id/targetBtn" />
    </android.support.constraint.ConstraintLayout>

    <!--<WebView-->
        <!--android:id="@+id/tetsss"-->
        <!--android:layout_width="match_parent"-->
        <!--android:layout_height="match_parent"/>-->

</android.support.constraint.ConstraintLayout> 

使用(做了存储权限判断):

MainActivity.java (其中点击相关事件和回调部分代码 )

private GuoQiCanvas guoQiCanvas;
guoQiCanvas = findViewById(R.id.aiguoCanvas);  
......

 /**
     * 选择合成点击事件
     *
     * @param view
     */
    public void PicOpt(View view) {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            // 未授权,申请授权(从相册选择图片需要读取存储卡的权限)
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 110);
        } else {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                    // 没有权限则申请权限
                    ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
                } else {
                    // 已授权,获取照片
                    switch (view.getId()) {
                        case R.id.originBtn:
                            choosePhoto(0);
                            break;
                        case R.id.targetBtn:
                            choosePhoto(1);
                            break;
                        case R.id.composeBtn:
                            guoQiCanvas.compose();
                            break;
                    }
                }
            }
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        // 获取图片成功进行绘制
        if (resultCode == RESULT_OK) {
            switch (requestCode) {
                case 0:
                case 1:
                    Uri uri = data.getData();
                    try {
                        Bitmap mBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
                        if (0 == requestCode) {
                            guoQiCanvas.drawSrcBitmap(mBitmap);
                        } else if (1 == requestCode) {
                            guoQiCanvas.drawDstBitmap(mBitmap);
                        }
                    } catch (IOException e) {
                        Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
                    }
                    break;
            }
        }
    }

    /**
     * 选择相册图片
     *
     * @param type
     */
    private void choosePhoto(int type) {
        Intent intentToPickPic = new Intent(Intent.ACTION_PICK, null);
        intentToPickPic.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
        startActivityForResult(intentToPickPic, type);
    }

权限配置:

AndroidManifest.xml

  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

基本就差不多了。。。我就瞎玩了一把。。哈哈。。。 放假了,还是要好好享受,多锻炼,保持身心健康...

工程地址: https://gitee.com/heyclock/doc/tree/master/微信_给我一面国旗

apk下载地址: https://heyclock.gitee.io/doc/微信_给我一面国旗/微信_给我一面国旗.apk

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
绘制飘动的国旗绘制总结 需求 将国旗图片在OpenGL中绘制出来,再实现飘动效果,最后增加半透明效果。 实现步骤 1. 首先确定使用分割法将整张图片分割成若干份,画在一个网格组成的长方形中 2. 画出N*N格的方格 3. 将国旗图案贴到这个网格中 a) 计算网格各顶点坐标 Vertex b) 计算各顶点对应的纹理坐标 texcoord c) 计算各顶点所对应的顶点法向量 Normal d) 计算出需要绘制的各顶点的顺序 order 4. 绘制飘动效果 a) 根据正弦曲线,计算出各顶点所对应的深度值 zDepth b) 计算出正弦曲线的波动 i. 坐标点的变动 ii. 顶点法向量坐标值的变动 5. 增加灯光效果 6. 增加材质的效果 遇到的问题 1. 确定网格的密度 网格的密度决定了波动的频率大小 2. 写出绘制的递归方法 开始实现时是通过循环绘制出每一个网格的纹理贴图,组合成整个图像,绘制效率较低且浪费资源较多,而且将图像进行旋转操作的时候会变形,不能达到预想的效果,所以开始对其进行整合 3. 计算各定点坐标时是使用三维数组的形式生成的,较简单,但是OpenGL绘制的时候需要的是buffer形式的,生成buffer时需要的是一维数组,java中有指针,所以需要自己写个方法把三维数组的内容转换到一维数组中,以求出buffer 4. 生成纹理坐标时比较简单,再生成顶点坐标的同时就可以生成,但是实现后遇到问题,问题贴图是上下颠倒的,所以需要与顶点对应的纹理坐标需要上下转换一下,虽然感觉比较麻烦,但是还是比较容易实现的 5. 计算各顶点所对应的法向量时遇到很大的麻烦,由于长时间有接触相关的数学知识,对法向量的概念有点生疏了,后来查找资料对法向量的概念进行复习,我写的生成顶点法向量的方法比较麻烦,写了5个方法来实现顶点法向量的计算,最后还是实现了 6. 各顶点顺序的计算,需要按照纹理贴图的相应顺序将顶点顺序确定出来,我用的最笨的方法得的,计算出2*2网格的定点坐标,根据坐标计算出顶点顺序,然后写出求顶点顺序的递归算法实现了顶点顺序的计算 7. 根据正弦曲线求出各点的深度,这个方法并不难写,但是在实现的时候,由于把顶点的顺序计算的有一点问题,画出来的图形在移动时不是按照正弦曲线的规律,使得波动的规律看起来极不自然,后来把顶点的计算改正后,还是比较美观的 8. 计算波动的时候,只需要将顶点的z轴坐标依次移动就可以了,比较容易实现,对于顶点法向量的移动,同样是后一个向前一个依次移动,但是移动的是x、y、z三个变量,我绘制的正好是两个波长的波动曲线,多以可以把第一个先缓存起来,赋值给最后一个即可,如果不是整波长的波动,可以把最后一列的顶点z值和顶点法向量重新计算,但是会影响一定的效率,所以建议整波长的比较好 9. 增加灯光效果和材质属性 由于之前有接触过,所以先进行了学习,然后再增加,遇到的问题是写增加灯光效果的时候,由于疏忽,三种光效属性中均写成了对环境光的修改,所以多费了些时间;在对材质属性进行增加的时候,由于对混合渲染不熟悉,尤其是对glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);两个参数所影响的色彩混合效果不明确,所以增加半透明效果的这块是试出来的。 10. 在不贴图的情况下,灯光效果比较明显,但是在贴图后,半透明效果有,但是灯光效果看不出来了,这块还有解决。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值