Android 不规则封闭区域填充 给图填色

2023-11-02 03:20

本文主要是介绍Android 不规则封闭区域填充 给图填色,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

点击跳转参考文章

  • 本文参考鸿洋大佬的博客 ,解决了当填充颜色一致时,连续点击同一位置会引发死循环/程序崩溃的bug ,并可以自定义一些色板

  • 效果图如下
    在这里插入图片描述

  • 布局代码如下

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"android:background="@color/back"tools:context=".MainActivity"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><RelativeLayoutandroid:layout_width="50dp"android:layout_height="50dp"><Buttonandroid:id="@+id/btn_red"android:layout_width="30dp"android:layout_height="30dp"android:layout_centerInParent="true"android:background="@drawable/btnred"/></RelativeLayout><RelativeLayoutandroid:layout_width="50dp"android:layout_height="50dp"><Buttonandroid:id="@+id/btn_green"android:layout_width="30dp"android:layout_height="30dp"android:layout_centerInParent="true"android:background="@drawable/btngreen"/></RelativeLayout><RelativeLayoutandroid:layout_width="50dp"android:layout_height="50dp"><Buttonandroid:id="@+id/btn_blue"android:layout_width="30dp"android:layout_height="30dp"android:layout_centerInParent="true"android:background="@drawable/btnblue"/></RelativeLayout><RelativeLayoutandroid:layout_width="50dp"android:layout_height="50dp"><Buttonandroid:id="@+id/btn_black"android:layout_width="30dp"android:layout_height="30dp"android:layout_centerInParent="true"android:background="@drawable/btnblack"/></RelativeLayout><RelativeLayoutandroid:layout_width="50dp"android:layout_height="50dp"><Buttonandroid:id="@+id/btn_white"android:layout_width="30dp"android:layout_height="30dp"android:layout_centerInParent="true"android:background="@drawable/btnwhite"/></RelativeLayout><Buttonandroid:layout_marginTop="10dp"android:id="@+id/suiji"android:layout_marginLeft="20dp"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/btn2"android:text="随机颜色"/></LinearLayout><!--    <Buttonandroid:id="@+id/onQiehuan"android:layout_marginTop="80dp"android:layout_marginLeft="10dp"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/btn1"android:text="切换图片"/>--><com.example.demoe.ColourImageViewandroid:id="@+id/image1"android:src="@drawable/image_007"android:background="#33ff0000"android:layout_width="match_parent"android:layout_centerInParent="true"android:layout_height="match_parent"/><com.example.demoe.ColourImageViewandroid:visibility="gone"android:id="@+id/image2"android:src="@drawable/bm_024"android:background="#33ff0000"android:layout_width="match_parent"android:layout_centerInParent="true"android:layout_height="match_parent"/><Buttonandroid:layout_marginTop="10dp"android:id="@+id/qiehuan"android:layout_marginLeft="20dp"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/btn1"android:layout_alignParentBottom="true"android:layout_marginBottom="20dp"android:text="切换图片"/><!--    <Buttonandroid:layout_marginTop="10dp"android:id="@+id/btn_save"android:layout_marginLeft="20dp"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/btn1"android:layout_alignParentRight="true"android:layout_alignParentBottom="true"android:layout_marginBottom="20dp"android:text="保存图片"android:layout_marginRight="20dp" />--></RelativeLayout>
  • 自定义一个ColourImageView类
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ImageView;import java.util.Random;
import java.util.Stack;@SuppressLint("AppCompatCustomView")
public class ColourImageView extends ImageView {private static int mtype;//1是自定义 2是随机private Bitmap mBitmap;private static int mColor=-1;//默认颜色是白色private static int oldColor;private  int newColor;private  int iswhith=0;private  int isheight=0;/*** 边界的颜色*/private int mBorderColor = -1;private boolean hasBorderColor = false;private Stack<Point> mStacks = new Stack<Point>();public ColourImageView(Context context, AttributeSet attrs){super(context, attrs);TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ColourImageView);mBorderColor = ta.getColor(R.styleable.ColourImageView_border_color, -1);hasBorderColor = (mBorderColor != -1);L.e("hasBorderColor = " + hasBorderColor + " , mBorderColor = " + mBorderColor);ta.recycle();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int viewWidth = getMeasuredWidth();int viewHeight = getMeasuredHeight();//以宽度为标准,等比例缩放view的高度setMeasuredDimension(viewWidth,getDrawable().getIntrinsicHeight() * viewWidth / getDrawable().getIntrinsicWidth());L.e("view's width = " + getMeasuredWidth() + " , view's height = " + getMeasuredHeight());//根据drawable,去得到一个和view一样大小的bitmapBitmapDrawable drawable = (BitmapDrawable) getDrawable();Bitmap bm = drawable.getBitmap();mBitmap = Bitmap.createScaledBitmap(bm, getMeasuredWidth(), getMeasuredHeight(), false);}@Overridepublic boolean onTouchEvent(MotionEvent event) {final int x = (int) event.getX();final int y = (int) event.getY();if (event.getAction() == MotionEvent.ACTION_DOWN) {fillColorToSameArea(x, y);}return super.onTouchEvent(event);}/*** 根据x,y获得改点颜色,进行填充** @param x* @param y*/private void fillColorToSameArea(int x, int y) {Bitmap bm = mBitmap;int pixel = bm.getPixel(x, y);if (pixel == Color.TRANSPARENT || (hasBorderColor && mBorderColor == pixel)){return;}if (mtype==1){newColor = mColor;}else {newColor = randomColor();}int w = bm.getWidth();int h = bm.getHeight();//拿到该bitmap的颜色数组int[] pixels = new int[w * h];bm.getPixels(pixels, 0, w, 0, 0, w, h);//填色fillColor(pixels, w, h, pixel, newColor, x, y);//重新设置bitmapbm.setPixels(pixels, 0, w, 0, 0, w, h);setImageDrawable(new BitmapDrawable(bm));}public static void getColorId(int acolor) {mColor=acolor;}public static void getType(int type) {mtype=type;}/*** @param pixels   像素数组* @param w        宽度* @param h        高度* @param pixel    当前点的颜色* @param newColor 填充色* @param i        横坐标* @param j        纵坐标*/private void fillColor(int[] pixels, int w, int h, int pixel, int newColor, int i, int j) {//步骤1:将种子点(x, y)入栈;mStacks.push(new Point(i, j));if (pixel==newColor && newColor==oldColor){return;}//步骤2:判断栈是否为空,// 如果栈为空则结束算法,否则取出栈顶元素作为当前扫描线的种子点(x, y),// y是当前的扫描线;while (!mStacks.isEmpty()) {/*** 步骤3:从种子点(x, y)出发,沿当前扫描线向左、右两个方向填充,* 直到边界。分别标记区段的左、右端点坐标为xLeft和xRight;*/Point seed = mStacks.pop();//L.e("seed = " + seed.x + " , seed = " + seed.y);int count = fillLineLeft(pixels, pixel, w, h, newColor, seed.x, seed.y);int left = seed.x - count + 1;count = fillLineRight(pixels, pixel, w, h, newColor, seed.x + 1, seed.y);int right = seed.x + count;oldColor=newColor;/*** 步骤4:* 分别检查与当前扫描线相邻的y - 1和y + 1两条扫描线在区间[xLeft, xRight]中的像素,* 从xRight开始向xLeft方向搜索,假设扫描的区间为AAABAAC(A为种子点颜色),* 那么将B和C前面的A作为种子点压入栈中,然后返回第(2)步;*///从y-1找种子if (seed.y - 1 >= 0){findSeedInNewLine(pixels, pixel, w, h, seed.y - 1, left, right);}//从y+1找种子if (seed.y + 1 < h){findSeedInNewLine(pixels, pixel, w, h, seed.y + 1, left, right);}}}/*** 在新行找种子节点** @param pixels* @param pixel* @param w* @param h* @param i* @param left* @param right*/private void findSeedInNewLine(int[] pixels, int pixel, int w, int h, int i, int left, int right) {/*** 获得该行的开始索引*/int begin = i * w + left;/*** 获得该行的结束索引*/int end = i * w + right;boolean hasSeed = false;int rx = -1, ry = -1;ry = i;/*** 从end到begin,找到种子节点入栈(AAABAAAB,则B前的A为种子节点)*/while (end >= begin) {if (pixels[end] == pixel) {if (!hasSeed) {rx = end % w;mStacks.push(new Point(rx, ry));hasSeed = true;}} else {hasSeed = false;}end--;}}/*** 往右填色,返回填充的个数** @return*/private int fillLineRight(int[] pixels, int pixel, int w, int h, int newColor, int x, int y) {int count = 0;while (x < w){//拿到索引int index = y * w + x;if (needFillPixel(pixels, pixel, index)){pixels[index] = newColor;count++;x++;} else{break;}}return count;}/*** 往左填色,返回填色的数量值** @return*/private int fillLineLeft(int[] pixels, int pixel, int w, int h, int newColor, int x, int y) {int count = 0;while (x >= 0){//计算出索引int index = y * w + x;if (needFillPixel(pixels, pixel, index)){pixels[index] = newColor;count++;x--;} else{break;}}return count;}private boolean needFillPixel(int[] pixels, int pixel, int index) {if (hasBorderColor){return pixels[index] != mBorderColor;} else{return pixels[index] == pixel;}}/*** 返回一个随机颜色** @return*/private static int randomColor() {Random random = new Random();int color = Color.argb(255, random.nextInt(256), random.nextInt(256), random.nextInt(256));return color;}
}
  • R.styleable.ColourImageView
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ColourImageView"><attr name="border_color" format="color|reference"></attr>
</declare-styleable>
</resources>
  • ColourImageView所在位置
    在这里插入图片描述
  • Log统一管理类
import android.util.Log;/*** Log统一管理类*/
public class L
{private L(){/* cannot be instantiated */throw new UnsupportedOperationException("cannot be instantiated");}public static boolean isDebug = true;// 是否需要打印bug,可以在application的onCreate函数里面初始化private static final String TAG = "fyh";// 下面四个是默认tag的函数public static void i(String msg){if (isDebug)Log.i(TAG, msg);}public static void d(String msg){if (isDebug)Log.d(TAG, msg);}public static void e(String msg){if (isDebug)Log.e(TAG, msg);}public static void v(String msg){if (isDebug)Log.v(TAG, msg);}// 下面是传入自定义tag的函数public static void i(String tag, String msg){if (isDebug)Log.i(tag, msg);}public static void d(String tag, String msg){if (isDebug)Log.i(tag, msg);}public static void e(String tag, String msg){if (isDebug)Log.i(tag, msg);}public static void v(String tag, String msg){if (isDebug)Log.i(tag, msg);}
}
  • MainActivity类的处理
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;public class MainActivity extends AppCompatActivity {private Button btn_red,btn_green,btn_blue,btn_black,suiji,btn_white,qiehuan;private int isqh=1;private ColourImageView image1,image2;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main2);initView();onClick();}private void initView() {btn_red=findViewById(R.id.btn_red);btn_green=findViewById(R.id.btn_green);btn_blue=findViewById(R.id.btn_blue);suiji=findViewById(R.id.suiji);btn_white=findViewById(R.id.btn_white);qiehuan=findViewById(R.id.qiehuan);image1=findViewById(R.id.image1);image2=findViewById(R.id.image2);btn_black=findViewById(R.id.btn_black);//默认给随机的动画Animation scale= AnimationUtils.loadAnimation(getApplication(),R.anim.scale);suiji.startAnimation(scale);}private void onClick() {btn_red.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {ColourImageView.getColorId(Color.RED);ColourImageView.getType(1);btn_red.setBackgroundResource(R.drawable.btnred1);btn_green.setBackgroundResource(R.drawable.btngreen);btn_blue.setBackgroundResource(R.drawable.btnblue);suiji.setBackgroundResource(R.drawable.btn1);btn_black.setBackgroundResource(R.drawable.btnblack);btn_white.setBackgroundResource(R.drawable.btnwhite);Animation scale= AnimationUtils.loadAnimation(getApplication(),R.anim.scale);btn_red.startAnimation(scale);btn_green.clearAnimation();btn_blue.clearAnimation();btn_black.clearAnimation();btn_white.clearAnimation();suiji.clearAnimation();}});btn_green.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {ColourImageView.getColorId(Color.GREEN);ColourImageView.getType(1);btn_red.setBackgroundResource(R.drawable.btnred);btn_green.setBackgroundResource(R.drawable.btngreen1);btn_blue.setBackgroundResource(R.drawable.btnblue);suiji.setBackgroundResource(R.drawable.btn1);btn_black.setBackgroundResource(R.drawable.btnblack);btn_white.setBackgroundResource(R.drawable.btnwhite);Animation scale= AnimationUtils.loadAnimation(getApplication(),R.anim.scale);btn_green.startAnimation(scale);btn_red.clearAnimation();btn_blue.clearAnimation();btn_black.clearAnimation();btn_white.clearAnimation();suiji.clearAnimation();}});btn_blue.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {ColourImageView.getColorId(Color.BLUE);ColourImageView.getType(1);btn_red.setBackgroundResource(R.drawable.btnred);btn_green.setBackgroundResource(R.drawable.btngreen);btn_blue.setBackgroundResource(R.drawable.btnblue1);suiji.setBackgroundResource(R.drawable.btn1);btn_black.setBackgroundResource(R.drawable.btnblack);btn_white.setBackgroundResource(R.drawable.btnwhite);Animation scale= AnimationUtils.loadAnimation(getApplication(),R.anim.scale);btn_blue.startAnimation(scale);btn_red.clearAnimation();btn_green.clearAnimation();btn_black.clearAnimation();btn_white.clearAnimation();suiji.clearAnimation();}});btn_black.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {ColourImageView.getColorId(Color.BLACK);ColourImageView.getType(1);suiji.setBackgroundResource(R.drawable.btn1);btn_red.setBackgroundResource(R.drawable.btnred);btn_green.setBackgroundResource(R.drawable.btngreen);btn_blue.setBackgroundResource(R.drawable.btnblue);btn_black.setBackgroundResource(R.drawable.btnblack1);btn_white.setBackgroundResource(R.drawable.btnwhite);Animation scale= AnimationUtils.loadAnimation(getApplication(),R.anim.scale);btn_black.startAnimation(scale);btn_red.clearAnimation();btn_green.clearAnimation();btn_blue.clearAnimation();btn_white.clearAnimation();suiji.clearAnimation();}});btn_white.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {ColourImageView.getColorId(Color.WHITE);ColourImageView.getType(1);suiji.setBackgroundResource(R.drawable.btn1);btn_red.setBackgroundResource(R.drawable.btnred);btn_green.setBackgroundResource(R.drawable.btngreen);btn_blue.setBackgroundResource(R.drawable.btnblue);btn_black.setBackgroundResource(R.drawable.btnblack);btn_white.setBackgroundResource(R.drawable.btnwhite1);Animation scale= AnimationUtils.loadAnimation(getApplication(),R.anim.scale);btn_white.startAnimation(scale);btn_red.clearAnimation();btn_green.clearAnimation();btn_blue.clearAnimation();btn_black.clearAnimation();suiji.clearAnimation();}});suiji.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {ColourImageView.getType(2);suiji.setBackgroundResource(R.drawable.btn2);btn_red.setBackgroundResource(R.drawable.btnred);btn_green.setBackgroundResource(R.drawable.btngreen);btn_blue.setBackgroundResource(R.drawable.btnblue);btn_black.setBackgroundResource(R.drawable.btnblack);btn_white.setBackgroundResource(R.drawable.btnwhite);Animation scale= AnimationUtils.loadAnimation(getApplication(),R.anim.scale);suiji.startAnimation(scale);btn_red.clearAnimation();btn_green.clearAnimation();btn_blue.clearAnimation();btn_black.clearAnimation();btn_white.clearAnimation();}});//切换图片qiehuan.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (isqh==1){image2.setVisibility(View.VISIBLE);image1.setVisibility(View.GONE);isqh=2;}else if(isqh==2){image1.setVisibility(View.VISIBLE);image2.setVisibility(View.GONE);isqh=1;}}});}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.menu_main, menu);return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {// Handle action bar item clicks here. The action bar will// automatically handle clicks on the Home/Up button, so long// as you specify a parent activity in AndroidManifest.xml.int id = item.getItemId();//noinspection SimplifiableIfStatementif (id == R.id.action_settings) {return true;}return super.onOptionsItemSelected(item);}@Overrideprotected void onDestroy() {super.onDestroy();btn_red.clearAnimation();btn_green.clearAnimation();btn_blue.clearAnimation();btn_black.clearAnimation();btn_white.clearAnimation();suiji.clearAnimation();}
}

这篇关于Android 不规则封闭区域填充 给图填色的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/327751

相关文章

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

Android里面的Service种类以及启动方式

《Android里面的Service种类以及启动方式》Android中的Service分为前台服务和后台服务,前台服务需要亮身份牌并显示通知,后台服务则有启动方式选择,包括startService和b... 目录一句话总结:一、Service 的两种类型:1. 前台服务(必须亮身份牌)2. 后台服务(偷偷干

Go Mongox轻松实现MongoDB的时间字段自动填充

《GoMongox轻松实现MongoDB的时间字段自动填充》这篇文章主要为大家详细介绍了Go语言如何使用mongox库,在插入和更新数据时自动填充时间字段,从而提升开发效率并减少重复代码,需要的可以... 目录前言时间字段填充规则Mongox 的安装使用 Mongox 进行插入操作使用 Mongox 进行更

Android kotlin语言实现删除文件的解决方案

《Androidkotlin语言实现删除文件的解决方案》:本文主要介绍Androidkotlin语言实现删除文件的解决方案,在项目开发过程中,尤其是需要跨平台协作的项目,那么删除用户指定的文件的... 目录一、前言二、适用环境三、模板内容1.权限申请2.Activity中的模板一、前言在项目开发过程中,尤

基于Java实现模板填充Word

《基于Java实现模板填充Word》这篇文章主要为大家详细介绍了如何用Java实现按产品经理提供的Word模板填充数据,并以word或pdf形式导出,有需要的小伙伴可以参考一下... Java实现按模板填充wor编程d本文讲解的需求是:我们需要把数据库中的某些数据按照 产品经理提供的 word模板,把数据

Android数据库Room的实际使用过程总结

《Android数据库Room的实际使用过程总结》这篇文章主要给大家介绍了关于Android数据库Room的实际使用过程,详细介绍了如何创建实体类、数据访问对象(DAO)和数据库抽象类,需要的朋友可以... 目录前言一、Room的基本使用1.项目配置2.创建实体类(Entity)3.创建数据访问对象(DAO

Android WebView的加载超时处理方案

《AndroidWebView的加载超时处理方案》在Android开发中,WebView是一个常用的组件,用于在应用中嵌入网页,然而,当网络状况不佳或页面加载过慢时,用户可能会遇到加载超时的问题,本... 目录引言一、WebView加载超时的原因二、加载超时处理方案1. 使用Handler和Timer进行超

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存