当前位置: 首页 > news >正文

Android 项目:画图白板APP开发(六)——分页展示

        本篇将介绍如何为我们的画板应用添加分页展示功能,让用户可以创建多个画布并在它们之间轻松切换。这章没有啥知识点的讲解,主要介绍一下每页保存的数据结构是什么样的。

一、ListView

        多页数据的管理我们使用ListView。之前有文章讲过ListView这里就不多赘述了,感兴趣的读者可以看看。Android最常用的控件ListView(详解) 。

直接上图例和代码:

//绑定适配器(传入handler)
adapter = new PictureAdapter(mContext,R.layout.list_item,listDate,handler);
viewMember.lv_tables.setAdapter(adapter);

(1)PictureView.java

//保存某一页的视图信息
public class PictureView {//保存比例信息Matrix matrixMain = new Matrix();//保存撤销和恢复的信息private ArrayList<PaintDates> paintedList = new ArrayList<>();public ArrayList<MessageStrokes> getCancelList() {return cancelList;}public void setCancelList(ArrayList<MessageStrokes> cancelList) {this.cancelList = cancelList;}public ArrayList<MessageStrokes> getRecoverList() {return recoverList;}public void setRecoverList(ArrayList<MessageStrokes> recoverList) {this.recoverList = recoverList;}private ArrayList<MessageStrokes> cancelList = new ArrayList<>();private ArrayList<MessageStrokes> recoverList = new ArrayList<>();//设置一个专门为撤销,回退服务的list//用来保存每一个操作的意义(可能是单笔的,可能是多笔)//view上private Bitmap cacheBitmap;private Canvas cacheCanvas ;public PictureView(int width, int height) {cacheBitmap = Bitmap.createBitmap(width,height,Bitmap.Config.ARGB_4444);cacheCanvas = new Canvas(cacheBitmap);}public ArrayList<PaintDates> getPaintedList() {return paintedList;}public void setPaintedList(ArrayList<PaintDates> paintedList) {this.paintedList = paintedList;}public Bitmap getCacheBitmap() {return cacheBitmap;}public void setCacheBitmap(Bitmap cacheBitmap) {this.cacheBitmap = cacheBitmap;}public Canvas getCacheCanvas() {return cacheCanvas;}public void setCacheCanvas(Canvas cacheCanvas) {this.cacheCanvas = cacheCanvas;}}

PictureView 是一个数据模型类,用于保存画板中某一页的完整状态信息。

  • (cacheBitmap 和 cacheCanvas):保存当前页面的最终渲染结果
  • paintedList:存储所有的笔画数据

  • cancelList存储已执行但可撤销的操作

  • recoverList存储已撤销但可恢复的操作

  • matrixMain保存缩放、平移、旋转等变换信息

(2)PictureAdapter.java

//适配器
public class PictureAdapter extends ArrayAdapter<PictureView> {//用来判断当前View上显示的时哪个(默认为第一个)public int localNum = 1;private Handler handler;public PictureAdapter(@NonNull Context context, int resource, @NonNull List<PictureView> objects, Handler handler) {super(context, resource, objects);this.handler = handler;}@SuppressLint("SetTextI18n")@NonNull@Overridepublic View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {PictureView p = getItem(position);View view;//新增一个内部类 ViewHolder,用于对控件的实例进行缓存ViewHolder viewHolder;if (convertView==null){//为每一个子项加载设定的布局view= LayoutInflater.from(getContext()).inflate(R.layout.list_item,parent,false);viewHolder= new ViewHolder();//分别获取 imageview 和 textview 的实例viewHolder.image =view.findViewById(R.id.iv_image);viewHolder.imageNum =view.findViewById(R.id.tv_num);viewHolder.imageDelete=view.findViewById(R.id.bt_delete_item);viewHolder.layout = view.findViewById(R.id.fl_item);view.setTag(viewHolder);//将 viewHolder 存储在 view 中}else {view=convertView;viewHolder= (ViewHolder) view.getTag();//重新获取 viewHolder}// 设置要显示的内容viewHolder.image.setImageBitmap(p.getCacheBitmap());viewHolder.imageNum.setText((position+1)+"");if((position+1)==localNum){//008FFBviewHolder.imageNum.setTextColor(Color.parseColor("#008FFB"));}else {viewHolder.imageNum.setTextColor(Color.WHITE);}//按钮点击事件(使用handler)viewHolder.imageDelete.setOnClickListener(v->{//创建一个线程Thread t = new Thread(() -> {Message m = handler.obtainMessage();m.what = 0x101;m.arg1 = position;handler.sendMessage(m);});t.start();});viewHolder.layout.setOnClickListener(v->{Thread t =new Thread(() -> {Message m = handler.obtainMessage();m.what = 0x102;m.arg1 = position;handler.sendMessage(m);});t.start();});return view;}private static class ViewHolder {TextView imageNum;ImageView image;ImageButton imageDelete;FrameLayout layout;}}

PictureAdapter中的点击事件,通过Handler传递

(3)Handler

@SuppressLint("HandlerLeak")
private void initHandler() {handler = new Handler(Looper.getMainLooper()){@SuppressLint("SetTextI18n")@Overridepublic void handleMessage(@NonNull Message msg) {switch (msg.what){case 0x101://删除//弹出提升框now = msg.arg1;new AlertDialog.Builder(mContext).setTitle("提示").setMessage("确定要删除 "+ (now+1) +"号视图吗?").setPositiveButton("确定", (dialogInterface, i) -> {//是否为删除的为当前显示的视图System.out.println("AAAAAAAAAA:   "+ (now+1) +" "+NowNum);if((now+1)==NowNum&&now==0){if(total == 1){Toast.makeText(mContext, "您无法删除最后一个视图", Toast.LENGTH_SHORT).show();}else {total--;//清空内存clearBitmap(listDate.get(now));listDate.remove(now);adapter.localNum = NowNum;blackboardView1.updateView(NowNum-1);}}else if((now+1)==NowNum&&now!=0){//向上移动total--;NowNum--;clearBitmap(listDate.get(now));listDate.remove(now);adapter.localNum = NowNum;blackboardView1.updateView(NowNum-1);}else if((now+1)!=NowNum&&(now+1)>NowNum){//不动total--;clearBitmap(listDate.get(now));listDate.remove(now);}else {//整体上移total--;NowNum--;clearBitmap(listDate.get(now));listDate.remove(now);adapter.localNum = NowNum;blackboardView1.updateView(NowNum-1);}viewMember.tv_whereForNum.setText(numToString(NowNum)+"/"+numToString(total));adapter.notifyDataSetChanged();}).setNegativeButton("取消",null).show();break;case 0x102://点击试图切换now = msg.arg1;NowNum = now+1;adapter.localNum = NowNum;//发送消息进行上传(目前感觉没必要上传)
//                        Message message = new Message();
//                        message.what = 16;
//                        message.arg1 = NowNum;
//                        System.out.println("popopopo nowNum:"+NowNum);
//                        //operateHandler//同时更新的还有底部的数字viewMember.tv_whereForNum.setText(numToString(NowNum)+"/"+numToString(total));
//                        oldBitmap = blackboardView1.cacheBitmap;
//                        operateHandler.sendEmptyMessage(100);//通知截屏上传(在没更新之前)blackboardView1.updateView(NowNum-1);adapter.notifyDataSetChanged();break;case 0x103://漫游:显示比例数值viewMember.tv_zoomNum.setText(FTOString((Float) msg.obj));break;case 0x104://down的是时候不让获取获取焦点viewMember.bt_tables.setEnabled(false);viewMember.bt_last.setEnabled(false);viewMember.bt_next.setEnabled(false);viewMember.bt_add.setEnabled(false);viewMember.tv_whereForNum.setEnabled(false);Resources resources_table = mContext.getResources();if (viewMember.lv_tables.getVisibility() == View.VISIBLE) {viewMember.lv_tables.setVisibility(View.GONE);viewMember.tv_whereForNum.setTextColor(Color.WHITE);}if(viewMember.ll_more.getVisibility()==View.VISIBLE){viewMember.ll_more.setVisibility(View.GONE);Drawable imageDrawable = resources_table.getDrawable(R.drawable.tables_uncheck);viewMember.bt_tables.setBackground(imageDrawable);}//开始工具类的按钮//1.首先要让下面一排子的东西点不了viewMember.bt_pen.setEnabled(false);viewMember.bt_eraser.setEnabled(false);viewMember.bt_revoke.setEnabled(false);viewMember.bt_recover.setEnabled(false);viewMember.bt_zoom.setEnabled(false);//2.布局恢复if(viewMember.ll_penWidth.getVisibility() == View.VISIBLE){//这个就证明在画笔的行列viewMember.bt_width_1.setEnabled(false);viewMember.bt_width_2.setEnabled(false);viewMember.bt_width_3.setEnabled(false);viewMember.bt_width_4.setEnabled(false);viewMember.bt_width_5.setEnabled(false);viewMember.bt_penColor.setEnabled(false);if(viewMember.ll_colorAndAlpha.getVisibility() == View.VISIBLE){viewMember.ll_colorAndAlpha.setVisibility(View.GONE);}}if(viewMember.ll_eraser.getVisibility() == View.VISIBLE){viewMember.bt_son_eraser.setEnabled(false);viewMember.bt_handwriting_eraser.setEnabled(false);viewMember.sb_clear_sliding.setEnabled(false);}break;case 0x105://up的时候解封viewMember.bt_tables.setEnabled(true);viewMember.bt_last.setEnabled(true);viewMember.bt_next.setEnabled(true);viewMember.bt_add.setEnabled(true);viewMember.tv_whereForNum.setEnabled(true);viewMember.bt_pen.setEnabled(true);viewMember.bt_eraser.setEnabled(true);viewMember.bt_revoke.setEnabled(true);viewMember.bt_recover.setEnabled(true);viewMember.bt_zoom.setEnabled(true);if(viewMember.ll_penWidth.getVisibility() == View.VISIBLE){//这个就证明在画笔的行列viewMember.bt_width_1.setEnabled(true);viewMember.bt_width_2.setEnabled(true);viewMember.bt_width_3.setEnabled(true);viewMember.bt_width_4.setEnabled(true);viewMember.bt_width_5.setEnabled(true);viewMember.bt_penColor.setEnabled(true);}if(viewMember.ll_eraser.getVisibility() == View.VISIBLE){viewMember.bt_son_eraser.setEnabled(true);viewMember.bt_handwriting_eraser.setEnabled(true);viewMember.sb_clear_sliding.setEnabled(true);}break;case 0x106: //电子笔清除屏幕new AlertDialog.Builder(mContext).setTitle("提示").setMessage("确定要清屏吗?").setPositiveButton("确定", (dialogInterface, i) -> {blackboardView1.clear();blackboardView1.isDialog = false;}).setNegativeButton("取消",(dialogInterface, i) -> {blackboardView1.isDialog = false;}).show().setCanceledOnTouchOutside (false);blackboardView1.clear_hardware();//清除其他笔画}super.handleMessage(msg);}};
}

Handler 涉及到了功能:这里涉及到后面要讲的功能,这里简单说下

  • 0x101 :当ListView点击删除时调用,弹出 AlertDialog 要求用户确定操作。

  • 0x102 :点击切换视图,主界面显示对应页的画布。

  • 0x103 :放大缩小时,显示比例数值。比如50% , 300%。

  • 0x104 :用户画线的时候,不允许操作ListView。

  • 0x105 :没有写画时允许操作。

  • 0x106:电子笔点击按钮后,调用清屏功能。

(4)更新画布 updateView

//切换视图,刷新
public void updateView(int whereView){//对所有数据进行更新ViewNum = whereView;mPaintedList = mListDate.get(ViewNum).getPaintedList();mCancelList = mListDate.get(ViewNum).getCancelList();mRecoverList = mListDate.get(ViewNum).getRecoverList();cacheBitmap = mListDate.get(ViewNum).getCacheBitmap();cacheCanvas = mListDate.get(ViewNum).getCacheCanvas();mMatrixMain = mListDate.get(ViewNum).matrixMain;//传一下handlermMatrixMain.getValues(mainDate);Message m = this.handler.obtainMessage();m.what = 0x103;m.obj = mainDate[0];this.handler.sendMessage(m);cacheCanvas.drawColor(0,PorterDuff.Mode.CLEAR);bottomCanvas.drawColor(0,PorterDuff.Mode.CLEAR);invalidateReason = REASON_RE;invalidate();
}

将 PictureView 中的对象赋值给当前视图即可。本章节篇幅较少,主要是介绍多画布的框架,为后面的章节打好基础。


文章转载自:

http://ZDogHIQF.ykmtz.cn
http://4D5xE4Ha.ykmtz.cn
http://zitmJR72.ykmtz.cn
http://7XwcW4ee.ykmtz.cn
http://IoYQcrym.ykmtz.cn
http://wEywcXF4.ykmtz.cn
http://azn7gScP.ykmtz.cn
http://Yu8EqHQu.ykmtz.cn
http://TMSht1Yd.ykmtz.cn
http://GGi1OpWS.ykmtz.cn
http://IdBddk0g.ykmtz.cn
http://N4H5TJon.ykmtz.cn
http://VgKhuvyf.ykmtz.cn
http://JmzMHSX9.ykmtz.cn
http://HGwYwLCb.ykmtz.cn
http://kWrzfKOC.ykmtz.cn
http://6gmFcAz7.ykmtz.cn
http://5C3QSPe6.ykmtz.cn
http://rYWGRWIk.ykmtz.cn
http://zybFZn8r.ykmtz.cn
http://EUyPsPmj.ykmtz.cn
http://UKvpNNWE.ykmtz.cn
http://PUDu2JVj.ykmtz.cn
http://yCZkdzq0.ykmtz.cn
http://ecSpSHRK.ykmtz.cn
http://6YwH9OyZ.ykmtz.cn
http://cplZHCXq.ykmtz.cn
http://h1GwOSgP.ykmtz.cn
http://xT3Vow14.ykmtz.cn
http://XFlbLl5J.ykmtz.cn
http://www.dtcms.com/a/378682.html

相关文章:

  • 阿里云ClickHouse数据保护秘籍:本地备份与恢复详解
  • 数字图像处理——图像金字塔
  • 全球充电标准体系简介
  • Sub-GHz无线收发单片机,低功耗物联网通信的硬件“基石”
  • React18学习笔记(一) 创建React项目,JSX基础应用,案例:视频网站评论区
  • 【实时Linux实战系列】规避缺页中断:mlock/hugetlb 与页面预热
  • 全球汽车高压电加热器市场规模到2031年将达到62.72亿美元,CAGR 25.2%
  • 【展厅多媒体】从技术到体验,AR在展厅中的一体化整合
  • 双指针算法_移动零
  • 数据结构之复杂度
  • 几种常用锁
  • android13修改WiFi扫描二维码识别识别成功率不高的问题
  • params和body传参讲解
  • 单片机学习笔记
  • 图卷积神经网络(GCN)学习笔记
  • MySQL执行过程中如何选择最佳的执行路径
  • 牛客周赛 Round 108(思维、位运算、DP、SOSDP)
  • 插槽 el-input 数据双向 绑定失效 响应式更新失败
  • 代码随想录算法训练营第58天 | 拓扑排序精讲、dijkstra(朴素版)精讲
  • 揭秘KafkaStreams 线程缓存:NamedCache深度解析
  • 中标麒麟7.4部署gitlab-runner
  • Shopify指纹手机矩阵:无限扩店,横扫FB/GG广告封号风险
  • react context如何使用
  • npm是什么?优缺点又是什么?
  • ubuntu24.04+5070ti训练yolo模型(2)
  • [SQL]查询SSMS当前连接数据库列表
  • 乾博绝缘监测仪为水泥厂安全生产护航
  • JVM(jdk1.8) 实战
  • 设计模式(C++)详解—工厂方法模式(2)
  • 自动化运维实践:SaaS系统Nginx配置文件自动化运维脚本详解