android studio安卓表情包编辑器

效果图

准备

我们先准备几张表情用ps把文字去掉


由于要用到recyclerview 先在app\build.gradle添加如下代码


implementation 'com.android.support:recyclerview-v7:26.1.0' 

在AndroidManifest添加

读写sd卡权限

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

这句代码加不加都可以,防止输入法挡住编辑框 不加的话看起来可能会有怪怪的感觉

android:windowSoftInputMode="adjustPan"

把刚刚我们准备好的图片导入进来


布局

新建布局main.xml(这是我们的主界面,activity_main.xml现在是多余的 不过我们先留着)

main.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">

    <com.wsbq.liziguo.hua
        android:id="@+id/hua"
        android:layout_width="280dp"
        android:layout_height="280dp"
        android:layout_gravity="center" >
    </com.wsbq.liziguo.hua>

    <TextView
        android:text="点击屏幕移动文字"
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <Button
            android:id="@+id/xiao"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="A-" />

        <Button
            android:id="@+id/bt"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="保存" />

        <Button
            android:id="@+id/da"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="A+" />
    </LinearLayout>


    <EditText
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="请输入" />


    <android.support.v7.widget.RecyclerView
        android:id="@+id/recy"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

大概长这个比样


然后改一下MainActivity onCreate方法里的

setContentView(R.layout.main);//这里改一下,使用的不是activity_main布局

然后写我们RecyclerView  Item里的布局

这里我们就不新建布局文件了,我们直接使用activity_main.xml

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    tools:context="com.wsbq.liziguo.MainActivity">

    <ImageView
        android:id="@+id/img"
        android:layout_width="180dp"
        android:layout_height="180dp" />

</LinearLayout>

里面就一个ImageView

好了,下面开始敲代码吧

新建类hua继承View,用来话表情和文字

hua.java

package com.wsbq.liziguo;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * Created by Liziguo on 2018/5/31.
 */

public class hua extends View {//这个view叫做“画”用来画表情和文字
    private int w, h;//view宽高
    public float x, y;//控制文本位置
    public Paint paint;//画笔
    public Bitmap img = null;//表情图片

    public hua(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
        paint.setColor(Color.BLACK);
        paint.setTextSize(70);//初始文字大小为70
//        setBackgroundColor(Color.WHITE);//一开始这个类的继承ViewGroup的 如果不添加这句图片画不出来
        setOnTouchListener(new OnTouchListener() {//添加触摸事件
            @Override
            public boolean onTouch(View v, MotionEvent e) {//调节文本位置
                x = e.getX() - paint.getTextSize();
                y = e.getY();
                invalidate();//刷新画布
                return true;
            }
        });
    }

    @Override
    protected void onDraw(Canvas canvas) {//画图像
        super.onDraw(canvas);
        if (img != null) canvas.drawBitmap(img, null, new RectF(0, 0, w, h), paint);//画表情图片,平铺整个view
        if (MainActivity.main.text != null) {
            String[] s = MainActivity.main.text.getText().toString().split("\n");//检测换行符,切割字符串
            for (int i = 0; i < s.length; i++) {
                canvas.drawText(s[i], x, y + i * paint.getTextSize(), paint);//这样就能显示多行文字了
            }
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        //将宽高保存起来
        this.w = w;
        this.h = h;
        y = h * 0.75f;//文字初始y轴坐标
    }

}

画布做好了 接下来编写MainActivity.java里的代码

MainActivity.java

package com.wsbq.liziguo;

import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Environment;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    public static MainActivity main;//当一个全局变量使用
    public hua hua;//这个view用来画图片和文字
    public EditText text;//用户输入的文字
    private RecyclerView recy;//滑动控件
    private List<Integer> list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        main = this;
        setContentView(R.layout.main);//这里改一下,使用的不是activity_main布局
        hua = findViewById(R.id.hua);
        text = findViewById(R.id.text);
        recy = findViewById(R.id.recy);
        //设为横向滚动
        recy.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
        //初始化表情图片,我们把他们的id存起来
        list = new ArrayList<Integer>();
        list.add(R.mipmap.jgz);
        list.add(R.mipmap.jh);
        list.add(R.mipmap.zxy);
        list.add(R.mipmap.jh2);
        list.add(R.mipmap.jh3);
        list.add(R.mipmap.jh4);
        list.add(R.mipmap.jgz2);
        list.add(R.mipmap.fls);
        list.add(R.mipmap.fls2);
        list.add(R.mipmap.ku);
        list.add(R.mipmap.zxy2);

        hua.img = BitmapFactory.decodeResource(getResources(), list.get(0));
        recy.setAdapter(new adapter(list));//设置适配器

        findViewById(R.id.xiao).setOnClickListener(new View.OnClickListener() {//添加Button事件 字体缩小
            @Override
            public void onClick(View v) {
                float f = hua.paint.getTextSize();
                if (f > 10) {
                    f--;
                }
                hua.paint.setTextSize(f);
                hua.invalidate();
            }
        });
        findViewById(R.id.da).setOnClickListener(new View.OnClickListener() {//添加Button事件 字体放大
            @Override
            public void onClick(View v) {
                float f = hua.paint.getTextSize();
                f++;
                hua.paint.setTextSize(f);
                hua.invalidate();
            }
        });
        findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {//添加Button事件 保存图片
            @Override
            public void onClick(View v) {
                hua.invalidate();//刷新画布
                hua.buildDrawingCache();//截图,区域是整个hua
                Bitmap img = hua.getDrawingCache();//获取调用buildDrawingCache()时截取的图片
                String name = "表情" + System.currentTimeMillis() + ".jpg";
                //文件的路径为 sd卡/表情包/
                File f = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/表情包/" + name);
                //判断文件父目录是否存在,不存在则创建
                if (!f.getParentFile().exists()) {
                    f.getParentFile().mkdirs();
                }
                try {
                    BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(f));
                    img.compress(Bitmap.CompressFormat.JPEG, 50, out);//参数(格式,品质,输出到流)
                    out.flush();
                    out.close();
                    Toast.makeText(getApplicationContext(), "图片已保存到" + f.getParent(), Toast.LENGTH_SHORT).show();
                    //下面这一段代码非常重要 不然图库/QQ/微信都找不到你的图片(重启手机才能看到)
                    //我们要发送一个广播通知图库刷新你的相册
                    //当然手机sd卡那么大全部扫描一遍的话很影响用户体验,所以这里我们只扫描刚刚输出的图片
                    Intent in = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(f));//发送广播通知图库刷新amssf
                    sendBroadcast(in);//发送广播
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        text.setSelection(text.getText().toString().length());
        verifyStoragePermissions(this);//动态申请权限
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //这一段代码是复制过来的 动态申请sd卡权限
    //设备系统是 Android 6.0 (API 23) 或更高版本,并且应用的 targetSdkVersion 是 23 或更高版本
    //则针对在 AndroidManifest.xml 中声明的危险权限,在运行时还需要动态请求用户授权。例如读写sd卡

    //这一段也是整个程序写好了才加上来的,因为这个软件在我手机和虚拟机上完美运行 到了别人手机就保存不了了
    //说好的Android 6.0呢?我手机Android 7.1.2都不用动态权限。。。
    private static final int REQUEST_EXTERNAL_STORAGE = 1;
    private static String[] PERMISSIONS_STORAGE = {
            "android.permission.READ_EXTERNAL_STORAGE",
            "android.permission.WRITE_EXTERNAL_STORAGE"};

    public static void verifyStoragePermissions(Activity activity) {

        try {
            //检测是否有写的权限
            int permission = ActivityCompat.checkSelfPermission(activity,
                    "android.permission.WRITE_EXTERNAL_STORAGE");
            if (permission != PackageManager.PERMISSION_GRANTED) {
                // 没有写的权限,去申请写的权限,会弹出对话框
                ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}

class adapter extends RecyclerView.Adapter<adapter.viewh> {//RecyclerView适配器
    private List<Integer> list;

    public adapter(List l) {
        list = l;
    }

    @Override
    public viewh onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.activity_main, parent, false);
        viewh h = new viewh(v);
        return h;
    }

    @Override
    public void onBindViewHolder(final viewh holder, int position) {
        final int i = list.get(position);
        holder.img.setImageResource(i);
        holder.itemView.setOnClickListener(new View.OnClickListener() {//更换图片点击事件。这样做会影响性能,先这样写吧
            @Override
            public void onClick(View v) {
                MainActivity.main.hua.img = BitmapFactory.decodeResource(MainActivity.main.getResources(), i);
                MainActivity.main.hua.invalidate();
            }
        });


    }

    @Override
    public int getItemCount() {//这里要改一下 不然显示不出来
        return list.size();
    }

    static class viewh extends RecyclerView.ViewHolder {
        ImageView img;
        View itemView;

        public viewh(View itemView) {
            super(itemView);
            img = itemView.findViewById(R.id.img);
            this.itemView = itemView;
        }
    }
}

大功告成

总结:做这个项目的时候碰了两次壁,一是申请动态权限不然有的手机可能保存不了,二是广播通知图库更新相册不然QQ微信找不到这张图片

下载地址:https://download.csdn.net/download/u010756046/10454405 (源码+素材)




猜你喜欢

转载自blog.csdn.net/u010756046/article/details/80547713