效果图
准备
我们先准备几张表情用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.youkuaiyun.com/download/u010756046/10454405 (源码+素材)