AndroidStudio实现在图片上涂鸦并记录涂鸦轨迹
生活随笔
收集整理的這篇文章主要介紹了
AndroidStudio实现在图片上涂鸦并记录涂鸦轨迹
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
AndroidStudio實現在圖片上涂鴉,并保存涂鴉軌跡
開個坑,終于有時間整理一下這個項目里用到的比較重要的技術
雖然最后甲方沒有采用(笑)
因為博主學藝不精,有很多小bug
AndroidStudio版本:2020.3.1.25
實現效果:
本文通過重寫view類,實現在選擇的圖片上涂鴉的功能,因為項目需要殘留了一些多余代碼
項目結構:
MainActivity為主程序類
HandWrite類為手寫類,用于處理各種手勢
res/layout/activity_main.xml 為主界面
res/xml/file_path.xml 為圖片緩存路徑
0.Handwrite類
劃重點,此類中重寫了view類,畫出了手指按下的軌跡,并記錄在pointlist中
package com.buildmaterialapplication;import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View;import java.util.ArrayList;public class HandWrite extends View {Paint paint = null; //定義畫筆Bitmap origBit = null; //存放原始圖像Bitmap new_1Bit = null; //存放從原始圖像復制的位圖圖像Bitmap new_2Bit = null; //存放處理后的圖像float startX = 0,startY = 0; //畫線的起點坐標float clickX = 0, clickY = 0; //畫線的終點坐標boolean isMove = false; //設置是否畫線的標記boolean isDown =false;boolean isClear = false; //設置是否清除涂鴉的標記int color = Color.BLUE; //設置畫筆的顏色float strokeWidth = 4.0f; //設置畫筆的寬度private int pen_type=0;ArrayList<Point> pointList =new ArrayList();Point point=new Point();public HandWrite(Context context, AttributeSet arrs){super(context,arrs);}//設置畫筆類型public void setPen_type(int x) {pen_type = x;Log.e("penType",Integer.toString(pen_type));}//構造public HandWrite(Context context, Bitmap bm, int type) {super(context);// 從資源中獲取原始圖像//origBit = BitmapFactory.decodeFile("/storage/emulated/0/Android/data/com.example.test/cache/19771639479845774.jpg").copy(Bitmap.Config.ARGB_8888,true);origBit=Bitmap.createBitmap(bm).copy(Bitmap.Config.ARGB_8888,true);//origBit = BitmapFactory.decodeResource(getResources(), R.drawable.p1).copy(Bitmap.Config.ARGB_8888,true);// 建立原始圖像的位圖new_1Bit = Bitmap.createBitmap(origBit);pen_type=type;}public Point getPoint() {return point;}public ArrayList<Point> getPointList() {return pointList;}// 清除涂鴉public void clear() {isClear = true;new_2Bit = Bitmap.createBitmap(origBit);invalidate();}//設置畫筆樣式public void setSytle(float strokeWidth) {this.strokeWidth = strokeWidth;this.color=Color.BLUE;}//重寫ondraw方法@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawBitmap(HandWriting(new_1Bit),0,0,null);}private Bitmap HandWriting(Bitmap origBit) { //記錄繪制圖形Canvas canvas = null; // 定義畫布if (isClear) { // 創建繪制新圖形的畫布canvas = new Canvas(new_2Bit);}else {canvas = new Canvas(origBit); //創建繪制原圖形的畫布}paint = new Paint();paint.setStyle(Paint.Style.STROKE);paint.setAntiAlias(true);paint.setColor(color);paint.setStrokeWidth(strokeWidth);//lassoif (isMove&&(pen_type==3)){canvas.drawLine(startX,startY,clickX,clickY,paint); // 在畫布上畫線條}//eraserif (isMove&&pen_type==2){paint.setAlpha(70);paint.setStrokeWidth(40.0f);paint.setStyle(Paint.Style.FILL);canvas.drawLine(startX,startY,clickX,clickY,paint); // 在畫布上畫線條}startX = clickX;startY = clickY;//penif (pen_type==1){if(isDown){paint.setColor(Color.GREEN);paint.setStyle(Paint.Style.FILL);canvas.drawCircle(clickX,clickY,20.0f,paint);}}//點擊的eraserif (isDown&&pen_type==2){paint.setColor(Color.BLUE);canvas.drawCircle(clickX,clickY,20.0f,paint);}if (isClear){return new_2Bit; // 返回新繪制的圖像}return origBit; // 若清屏,則返回原圖像}@Overridepublic boolean onTouchEvent(MotionEvent event) {final int historySize = event.getHistorySize();final int pointerCount = event.getPointerCount();for (int h = 0; h < historySize; h++) { // Log.e("At time %d:", (event.getHistoricalEventTime(h)));for (int p = 0; p < pointerCount; p++) {/*System.out.printf(" pointer %d: (%f,%f)",event.getPointerId(p), event.getHistoricalX(p, h), event.getHistoricalY(p, h));*/int po=(event.getPointerId(p));Float ghx=event.getHistoricalX(p, h);Float ghy=event.getHistoricalY(p, h);String pointer =Integer.toString(po);String x=Float.toString(ghx);String y=Float.toString(ghy);String tmp1=pointer+x+y; // Log.e("pointer1",tmp1);}}int time= (int) event.getEventTime(); // String tmp2=Integer.toString(time); // Log.e("time",tmp2);//System.out.printf("At time %d:", event.getEventTime());//獲取點集for (int p = 0; p < pointerCount; p++) {int po=(event.getPointerId(p));int ghx=(int) event.getX(p);int ghy=(int) event.getY(p);Point point=new Point(ghx,ghy);if(!pointList.contains(point)){if(pen_type!=2){pointList.add(p,point);int x=pointList.get(p).x;int y=pointList.get(p).y;Log.e("po",Integer.toString(x)+" "+Integer.toString(y));}} // String pointer =Integer.toString(po);/*System.out.printf(" pointer %d: (%f,%f)",event.getPointerId(p), event.getX(p), event.getY(p));*/}clickX = event.getX(); // 獲取觸摸坐標位置clickY = event.getY();if(event.getAction() == MotionEvent.ACTION_DOWN){isMove=false;isDown=true;invalidate();return true;}else if (event.getAction() == MotionEvent.ACTION_MOVE) { // 記錄在屏幕上劃動的軌跡isMove = true;isDown=false;invalidate();return true;}if(event.getAction()==MotionEvent.ACTION_UP){performClick();invalidate();return true;}return super.onTouchEvent(event);} }1.activity_main.xml界面
<?xml version="1.0" encoding="utf-8"?><LinearLayout 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:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".MainActivity"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="50dp"android:gravity="center"android:orientation="horizontal"></LinearLayout><LinearLayoutandroid:id="@+id/hw"android:layout_width="300dp"android:layout_height="500dp"android:layout_gravity="center"android:gravity="center"android:orientation="horizontal"></LinearLayout><TextViewandroid:id="@+id/txt_result"android:layout_gravity="center"android:layout_marginTop="@dimen/space"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="@dimen/txt_choose"android:textColor="@color/black"></TextView><LinearLayoutandroid:layout_width="match_parent"android:layout_height="100dp"android:layout_marginBottom="100dp"android:gravity="center"android:orientation="horizontal"><LinearLayoutandroid:layout_width="@dimen/icon_loc"android:layout_height="match_parent"android:gravity="center"android:orientation="vertical"><Buttonandroid:id="@+id/icon_lasso"android:layout_width="@dimen/icon_size"android:layout_height="@dimen/icon_size"android:background="@drawable/ic_lasso"></Button><TextViewandroid:id="@+id/txt_lasso"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="@dimen/space"android:text="@string/txt_lasso"android:textColor="@color/black"android:textSize="@dimen/btn_txt_size"></TextView></LinearLayout><LinearLayoutandroid:layout_width="@dimen/icon_loc"android:layout_height="match_parent"android:gravity="center"android:orientation="vertical"><Buttonandroid:id="@+id/icon_pen"android:layout_width="@dimen/icon_size"android:layout_height="@dimen/icon_size"android:background="@drawable/ic_pen"></Button><TextViewandroid:id="@+id/txt_pen"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="@dimen/space"android:text="@string/txt_pen"android:textColor="@color/black"android:textSize="@dimen/btn_txt_size"></TextView></LinearLayout><LinearLayoutandroid:layout_width="@dimen/icon_loc"android:layout_height="match_parent"android:gravity="center"android:orientation="vertical"><Buttonandroid:id="@+id/icon_next"android:layout_width="@dimen/icon_size"android:layout_height="@dimen/icon_size"android:gravity="center"android:background="@drawable/ic_next"></Button><TextViewandroid:id="@+id/txt_next"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="@dimen/space"android:text="@string/txt_next"android:textColor="@color/black"android:textSize="@dimen/btn_txt_size"></TextView></LinearLayout></LinearLayout> </LinearLayout>2.file_paths.xml圖片存儲路徑
<?xml version="1.0" encoding="utf-8"?> <resources><external-cache-path path="." name="take_photo"/> </resources>3.mainfest文件
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"package="com.buildmaterialapplication"><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"tools:ignore="ProtectedPermissions"/><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.CAMERA"/><uses-permission android:name="android.permission.INTERNET" /><applicationandroid:allowBackup="true"android:icon="@drawable/ic_car"android:label="@string/app_name"android:roundIcon="@drawable/ic_car"android:supportsRtl="true"android:theme="@style/Theme.MyApplication"android:requestLegacyExternalStorage="true"android:usesCleartextTraffic="true"android:hardwareAccelerated="false"android:largeHeap="true"><activityandroid:name=".MainActivity"android:exported="true"tools:ignore="DuplicateActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><providerandroid:authorities="com.buildmaterialapplication.fileprovider"android:name="androidx.core.content.FileProvider"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/file_paths"/></provider></application><supports-screens android:resizeable="true" /> </manifest>4.MainActivity
package com.buildmaterialapplication;import android.Manifest; import android.annotation.SuppressLint; import android.content.DialogInterface; import android.content.Intent; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; import android.graphics.Matrix; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.MediaStore; import android.util.DisplayMetrics; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView;import org.jetbrains.annotations.Nullable; import org.opencv.core.Mat;import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.Objects;import androidx.annotation.RequiresApi; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.FileProvider;//閱讀前請查看README文件 public class MainActivity extends AppCompatActivity {//spinner用的列表private final static String[] items = new String[]{"拍照","從相冊中選擇",};public static final int TAKE_PHOTO=1;//聲明一個請求碼,用于識別返回的結果private static final int SCAN_OPEN_PHONE = 2;// 相冊private Uri imageUri;public String path=null;Bitmap bitmap=null;public int count=0;public String picpath=null;private LinearLayout handWrite=null;public HandWrite hd;public HandWrite ori_hd; //當前顯示的handwrite控件public boolean isLasso=false; //是否選擇范圍int requestW=0; //初始化頁面顯示的圖片的寬高int requestH=0;int drawType=0; //畫筆類型 1畫筆 2橡皮 3套索@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);askPermission();//用于設置圖片顯示的大小DisplayMetrics dm=getResources().getDisplayMetrics();requestW=(int) (dm.widthPixels*0.8);requestH=(int) (dm.heightPixels*0.6);//加載手寫類控件handWrite=findViewById(R.id.hw);choosePic();//aiAlgorithmbuttonEvent();if(drawType==0){TextView txt_pen=findViewById(R.id.txt_pen);txt_pen.setTextColor(Color.BLACK);}}/*** @name: askPermission* @param :null* @return :null* @describe: 請求相機等權限*/private void askPermission(){ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.CAMERA},0);}/*** @name: buttonEvent* @param :null* @return null* @describe: 按鈕點擊事件*/public void buttonEvent() {Button btn_pen;Button btn_lasso;Button btn_eraser;TextView txt_pen = findViewById(R.id.txt_pen);TextView txt_lasso = findViewById(R.id.txt_lasso);btn_pen = (Button) findViewById(R.id.icon_pen);//畫筆事件btn_pen.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {txt_pen.setTextColor(Color.GRAY);txt_lasso.setTextColor(Color.BLACK);drawType = 1;ori_hd.setPen_type(drawType);}});//套索事件btn_lasso = (Button) findViewById(R.id.icon_lasso);btn_lasso.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {txt_lasso.setTextColor(Color.GRAY);txt_pen.setTextColor(Color.BLACK); // draw_type=0;drawType = 3;ori_hd.setPen_type(drawType);isLasso = true;}});//下一張Button ic_next = (Button) findViewById(R.id.icon_next);ic_next.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {startActivity(new Intent(com.buildmaterialapplication.MainActivity.this, com.buildmaterialapplication.MainActivity.class));}});}/*** @name: choosePic* @params:* @return* @describe: 選擇照片或拍照*/private void choosePic(){count=0;AlertDialog.Builder builder = new AlertDialog.Builder(com.buildmaterialapplication.MainActivity.this).setTitle("請選擇圖片")//設置對話框 標題.setItems(items, new DialogInterface.OnClickListener() {@RequiresApi(api = Build.VERSION_CODES.N)@Overridepublic void onClick(DialogInterface dialog, int which) {if(which==0){openCamera();}else{openGallery();}return;}});builder.create().show();}/*** @name: openGallery* @params:* @return* @describe: 拍照*/private void openGallery() {Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);//intent.setType("image/*");startActivityForResult(intent, SCAN_OPEN_PHONE);}/*** @name: openCamera* @params:* @return* @describe: 拍照*/@RequiresApi(api = Build.VERSION_CODES.N)private void openCamera(){String imageName = new SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).format(new Date()); // File outputImage=new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/data/com.example.woundapplication/"+imageName+".jpg");File outputImage = new File(getExternalCacheDir(), imageName+".jpg");Objects.requireNonNull(outputImage.getParentFile()).mkdirs(); // Log.e("", outputImage.getAbsolutePath());/*創建一個File文件對象,用于存放攝像頭拍下的圖片,我們把這個圖片命名為output_image.jpg并把它存放在應用關聯緩存目錄下,調用getExternalCacheDir()可以得到這個目錄,為什么要用關聯緩存目錄呢?由于android6.0開始,讀寫sd卡列為了危險權限,使用的時候必須要有權限,應用關聯目錄則可以跳過這一步*/try//判斷圖片是否存在,存在則刪除在創建,不存在則直接創建{if(outputImage.exists()){outputImage.delete();}boolean a = outputImage.createNewFile(); // Log.e("createNewFile", String.valueOf(a));}catch (IOException e){e.printStackTrace();}if(Build.VERSION.SDK_INT>=24)//判斷安卓的版本是否高于7.0,高于則調用高于的方法,低于則調用低于的方法//把文件轉換成Uri對象/*之所以這樣,是因為android7.0以后直接使用本地真實路徑是不安全的,會拋出異常。FileProvider是一種特殊的內容提供器,可以對數據進行保護*/{imageUri= FileProvider.getUriForFile(com.buildmaterialapplication.MainActivity.this,"com.buildmaterialapplication.fileprovider",outputImage); // imageUri=Uri.fromFile(outputImage);path=imageUri.getPath();Log.e(">7:",path);/*第一個參數:context對象第二個參數:任意唯一的字符串第三個參數:文件對象*/}else {imageUri= Uri.fromFile(outputImage);path=imageUri.getPath();Log.e("<7:",imageUri.getPath());}//使用隱示的Intent,系統會找到與它對應的活動,即調用攝像頭,并把它存儲Intent intent0=new Intent("android.media.action.IMAGE_CAPTURE"); // Intent intent0=new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);intent0.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);startActivityForResult(intent0,TAKE_PHOTO);//調用會返回結果的開啟方式,返回成功的話,則把它顯示出來Log.e("pic",path);}//拍照或相冊的響應事件@SuppressLint("SetTextI18n")protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {super.onActivityResult(requestCode, resultCode, data);Bitmap bitmaptmp;switch (requestCode) {case TAKE_PHOTO:if (resultCode == RESULT_OK) {//將圖片解析成Bitmap對象,并把它顯現出來try {bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));} catch (FileNotFoundException e) {e.printStackTrace();}picpath= com.buildmaterialapplication.MainActivity.this.getExternalCacheDir().getPath()+imageUri.getPath(); // bitmap =BitmapFactory.decodeStream(filePath);Log.e("filename",picpath);@SuppressLint("SdCardPath") String fileName = picpath;//縮放bitmap=getScaleBitmap(bitmap,requestW,requestH);Mat m=new Mat();hd=new HandWrite(com.buildmaterialapplication.MainActivity.this,bitmap,0);handWrite.addView(hd);ori_hd=hd;}break;//相冊的響應case SCAN_OPEN_PHONE:if (resultCode == RESULT_OK){Uri selectImage=data.getData();String[] FilePathColumn={MediaStore.Images.Media.DATA};Cursor cursor = getContentResolver().query(selectImage,FilePathColumn, null, null, null);cursor.moveToFirst();//從數據視圖中獲取已選擇圖片的路徑int columnIndex = cursor.getColumnIndex(FilePathColumn[0]);picpath = cursor.getString(columnIndex);Log.e("picpath",picpath);cursor.close();bitmaptmp=BitmapFactory.decodeFile(picpath);bitmap=getScaleBitmap(bitmaptmp,requestW,requestH);//預處理hd=new HandWrite(com.buildmaterialapplication.MainActivity.this,bitmap,0);handWrite.addView(hd);ori_hd=hd;}break;default:break;}}/*** @name: getScaleBitmap* @params:* sourceBitmap:原圖* width: 需要的寬* height: 需要的高* @return* @describe: bitmap縮放函數*/private Bitmap getScaleBitmap(Bitmap sourceBitmap,float width,float height){Bitmap scaleBitmap;Matrix matrix = new Matrix();float scale= 0;if(sourceBitmap.getWidth()>sourceBitmap.getHeight()){scale=width/sourceBitmap.getWidth();}else{scale=height/ sourceBitmap.getHeight();} // float scale_x = width/sourceBitmap.getWidth(); // float scale_y = height/sourceBitmap.getHeight();matrix.postScale(scale,scale);try {scaleBitmap = Bitmap.createBitmap(sourceBitmap,0,0,sourceBitmap.getWidth(),sourceBitmap.getHeight(),matrix,true);}catch (OutOfMemoryError e){scaleBitmap = null;System.gc();}return scaleBitmap;} }結語
最后只保留了涂鴉的部分,擦除有點bug
不想讀取手機中的照片,想直接用電腦上的照片的話,改掉handwrite類里的構造方法就好了,注釋中有寫使用本地資源的方法,
代碼還是有很多問題的,handwrite類的刷新寫的也很麻煩,如果不是學習Android的話,盡量不要用這么底層的方法去寫
總結
以上是生活随笔為你收集整理的AndroidStudio实现在图片上涂鸦并记录涂鸦轨迹的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 虚拟机安装LEDE之后如何配置连接互联网
- 下一篇: [css] 用CSS画出一个任意角度的