整体思路
1、获取要截图的view
2、根据这个view创建Bitmap
3、保存图片,拿到图片路径
4、把图片路径传入自定义view(自定义view实现的功能:画圆角边框,动画缩小至消失)
主要用到的是ObjectAnimator属性动画的缩小和平移
核心代码
得到图片的路径
private String getFilePath() { Bitmap bitmap = createViewBitmap(picImg); if (bitmap != null) { try { // 首先保存图片 String storePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "HIS"; File appDir = new File(storePath); if (!appDir.exists()) { appDir.mkdir(); } String fileName = System.currentTimeMillis() + ".jpg"; File file = new File(appDir, fileName); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file)); bitmap.compress(Bitmap.CompressFormat.JPEG, 80, bos); bos.flush(); bos.close(); Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); Uri uri = Uri.fromFile(file); intent.setData(uri); sendBroadcast(intent); return file.getAbsolutePath(); } catch (Exception e) { return null; } } else { return null; } } public Bitmap createViewBitmap(View v) { Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); v.draw(canvas); return bitmap; }
把图片路径传入自定义view
String filePath = getFilePath(); mDisplayScreenshotSnv.setVisibility(View.GONE); mDisplayScreenshotSnv.setPath(filePath, picImg.getMeasuredWidth(), picImg.getMeasuredHeight(), true); mDisplayScreenshotSnv.setVisibility(View.VISIBLE);
截图实现圆角边框和动画消失
//实现截图动画(添加圆角边框) Glide.with(getContext()) .load(new File(path)) .transform(new CenterCrop(getContext()), new GlideRoundTransform(getContext(), radius)) .crossFade() .listener(new RequestListener<File, GlideDrawable>() { @Override public boolean onException(Exception e, File model, Target<GlideDrawable> target, boolean isFirstResource) { if (anim) { anim(thumb, true); } return false; } @Override public boolean onResourceReady(GlideDrawable resource, File model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) { if (thumb.getDrawable() == null) { // 避免截图成功时出现短暂的全屏白色背景 thumb.setImageDrawable(resource); } if (anim) { anim(thumb, true); } return false; } }).into(thumb); //启动延时关闭截图(显示5秒消失截图) startTick(true);
动画设置
/** * 动画设置 * @param view * @param start */ private void anim(final ImageView view, boolean start) { if (!start) { if (getChildCount() > 0) { // 快速点击截图时,上一次添加的子视图尚未移除,需重置视图 resetView(); } setScaleX(1f); setScaleY(1f); setTranslationX(0f); setTranslationY(0f); clearAnimation(); if (mScaleXAnim != null) { mScaleXAnim.cancel(); mScaleXAnim = null; } if (mScaleYAnim != null) { mScaleYAnim.cancel(); mScaleYAnim = null; } if (mTranslationXAnim != null) { mTranslationXAnim.cancel(); mTranslationXAnim = null; } if (mTranslationYAnim != null) { mTranslationYAnim.cancel(); mTranslationYAnim = null; } return; } view.post(new Runnable() { @Override public void run() { if (!view.isAttachedToWindow()) { // 子视图已被移除 return; } setCardBackgroundColor(Color.WHITE); //等待cross fade动画 float margins = DisplayUtil.dip2px(getContext(), 10); float scaleToX = (float) mFinalW / getMeasuredWidth(); float scaleToY = (float) mFinalH / getMeasuredHeight(); float translateToX = -(getMeasuredWidth() / 2f - (mFinalW / 2 + margins)); float translateToY = getMeasuredHeight() / 2f - (mFinalH / 2f + margins); //以当前view为中心,x轴右为正,左为负;y轴下为正,上为负 mScaleXAnim = ObjectAnimator.ofFloat(ScreenshotNotifyView.this, "scaleX", 1.0f, scaleToX); mScaleYAnim = ObjectAnimator.ofFloat(ScreenshotNotifyView.this, "scaleY", 1.0f, scaleToY); mTranslationXAnim = ObjectAnimator.ofFloat(ScreenshotNotifyView.this, "translationX", 1.0f, translateToX); mTranslationYAnim = ObjectAnimator.ofFloat(ScreenshotNotifyView.this, "translationY", 1.0f, translateToY); //设置速度 mScaleXAnim.setDuration(500); mScaleYAnim.setDuration(500); mTranslationXAnim.setDuration(500); mTranslationYAnim.setDuration(500); //缩放 mScaleXAnim.start(); mScaleYAnim.start(); //平移 mTranslationXAnim.start(); mTranslationYAnim.start(); setEnabled(false); mScaleXAnim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationCancel(Animator animation) { super.onAnimationCancel(animation); setEnabled(true); } @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); setEnabled(true); setClickable(true); } @Override public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); } }); } }); }
完整代码
ScreenshotNotifyView.java
package com.lyw.myproject.widget; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Color; import android.os.Handler; import android.os.Looper; import android.util.AttributeSet; import android.view.Gravity; import android.widget.FrameLayout; import android.widget.ImageView; import com.bumptech.glide.Glide; import com.bumptech.glide.load.resource.bitmap.CenterCrop; import com.bumptech.glide.load.resource.drawable.GlideDrawable; import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.target.Target; import com.lyw.myproject.screenshot.GlideRoundTransform; import com.lyw.myproject.utils.DisplayUtil; import java.io.File; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.cardview.widget.CardView; public class ScreenshotNotifyView extends CardView { private int mFinalW; private int mFinalH; private Handler mHandler; private ObjectAnimator mScaleXAnim = null; private ObjectAnimator mScaleYAnim = null; private ObjectAnimator mTranslationXAnim = null; private ObjectAnimator mTranslationYAnim = null; public ScreenshotNotifyView(@NonNull Context context) { super(context); init(context); } public ScreenshotNotifyView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context); } public ScreenshotNotifyView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } @Override public void setVisibility(int visibility) { super.setVisibility(visibility); if (visibility == GONE) { resetView(); } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); anim(null, false); startTick(false); } private void init(Context context) { mHandler = new Handler(Looper.getMainLooper()); setCardElevation(0); } public void setPath(final String path, int w, int h, final boolean anim) { setClickable(false); anim(null, false); final ImageView thumb = new ImageView(getContext()); FrameLayout.LayoutParams thumbParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) getLayoutParams(); int padding = (int) DisplayUtil.dip2px(getContext(), 2); int margins = (int) DisplayUtil.dip2px(getContext(), 8); //设置截图之后的宽度,高度按照比例设置 mFinalW = (int) DisplayUtil.dip2px(getContext(), 90); mFinalH = (int) ((float) mFinalW * h) / w; if (!anim) { //设置边框 params.setMargins(margins, margins, margins, margins); margins = (int) DisplayUtil.dip2px(getContext(), 2); params.width = mFinalW + margins * 2; params.height = mFinalH + margins * 2; params.gravity = Gravity.START | Gravity.BOTTOM; thumbParams.width = mFinalW; thumbParams.height = mFinalH; thumbParams.gravity = Gravity.CENTER; setLayoutParams(params); requestLayout(); } else { //设置边框 thumbParams.setMargins(margins, margins, margins, margins); params.setMargins(0, 0, 0, 0); params.width = FrameLayout.LayoutParams.MATCH_PARENT; params.height = FrameLayout.LayoutParams.MATCH_PARENT; setLayoutParams(params); requestLayout(); } thumb.setScaleType(ImageView.ScaleType.FIT_XY); thumb.setLayoutParams(thumbParams); addView(thumb); post(new Runnable() { @Override public void run() { float scale = (float) mFinalW / getMeasuredWidth(); int radius = 5; if (anim) { radius = (int) (5f / scale); } setRadius((int) DisplayUtil.dip2px(getContext(), radius)); //显示截图(添加圆角) Glide.with(getContext()) .load(new File(path)) .transform(new CenterCrop(getContext()), new GlideRoundTransform(getContext(), radius)) .crossFade() .listener(new RequestListener<File, GlideDrawable>() { @Override public boolean onException(Exception e, File model, Target<GlideDrawable> target, boolean isFirstResource) { if (anim) { anim(thumb, true); } return false; } @Override public boolean onResourceReady(GlideDrawable resource, File model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) { if (thumb.getDrawable() == null) { // 避免截图成功时出现短暂的全屏白色背景 thumb.setImageDrawable(resource); } if (anim) { anim(thumb, true); } return false; } }).into(thumb); //启动延时关闭截图(显示5秒消失截图) startTick(true); } }); } /** * 动画设置 * @param view * @param start */ private void anim(final ImageView view, boolean start) { if (!start) { if (getChildCount() > 0) { // 快速点击截图时,上一次添加的子视图尚未移除,需重置视图 resetView(); } setScaleX(1f); setScaleY(1f); setTranslationX(0f); setTranslationY(0f); clearAnimation(); if (mScaleXAnim != null) { mScaleXAnim.cancel(); mScaleXAnim = null; } if (mScaleYAnim != null) { mScaleYAnim.cancel(); mScaleYAnim = null; } if (mTranslationXAnim != null) { mTranslationXAnim.cancel(); mTranslationXAnim = null; } if (mTranslationYAnim != null) { mTranslationYAnim.cancel(); mTranslationYAnim = null; } return; } view.post(new Runnable() { @Override public void run() { if (!view.isAttachedToWindow()) { // 子视图已被移除 return; } setCardBackgroundColor(Color.WHITE); //等待cross fade动画 float margins = DisplayUtil.dip2px(getContext(), 10); float scaleToX = (float) mFinalW / getMeasuredWidth(); float scaleToY = (float) mFinalH / getMeasuredHeight(); float translateToX = -(getMeasuredWidth() / 2f - (mFinalW / 2 + margins)); float translateToY = getMeasuredHeight() / 2f - (mFinalH / 2f + margins); //以当前view为中心,x轴右为正,左为负;y轴下为正,上为负 mScaleXAnim = ObjectAnimator.ofFloat(ScreenshotNotifyView.this, "scaleX", 1.0f, scaleToX); mScaleYAnim = ObjectAnimator.ofFloat(ScreenshotNotifyView.this, "scaleY", 1.0f, scaleToY); mTranslationXAnim = ObjectAnimator.ofFloat(ScreenshotNotifyView.this, "translationX", 1.0f, translateToX); mTranslationYAnim = ObjectAnimator.ofFloat(ScreenshotNotifyView.this, "translationY", 1.0f, translateToY); //设置速度 mScaleXAnim.setDuration(500); mScaleYAnim.setDuration(500); mTranslationXAnim.setDuration(500); mTranslationYAnim.setDuration(500); //缩放 mScaleXAnim.start(); mScaleYAnim.start(); //平移 mTranslationXAnim.start(); mTranslationYAnim.start(); setEnabled(false); mScaleXAnim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationCancel(Animator animation) { super.onAnimationCancel(animation); setEnabled(true); } @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); setEnabled(true); setClickable(true); } @Override public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); } }); } }); } private void resetView() { setCardBackgroundColor(Color.TRANSPARENT); removeAllViews(); startTick(false); } private void startTick(boolean start) { if (!start) { mHandler.removeCallbacksAndMessages(null); return; } mHandler.postDelayed(new Runnable() { @Override public void run() { setVisibility(GONE); } }, 5 * 1000); } }
GlideRoundTransform.java
package com.lyw.myproject.screenshot; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.RectF; import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; /** * Created on 2018/12/26. * * @author lyw **/ public class GlideRoundTransform extends BitmapTransformation { private static float radius = 0f; /** * 构造函数 默认圆角半径 4dp * * @param context Context */ public GlideRoundTransform(Context context) { this(context, 4); } /** * 构造函数 * * @param context Context * @param dp 圆角半径 */ public GlideRoundTransform(Context context, int dp) { super(context); radius = Resources.getSystem().getDisplayMetrics().density * dp; } @Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { return roundCrop(pool, toTransform); } private static Bitmap roundCrop(BitmapPool pool, Bitmap source) { if (source == null) { return null; } Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888); if (result == null) { result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888); } Canvas canvas = new Canvas(result); Paint paint = new Paint(); paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP)); paint.setAntiAlias(true); RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight()); canvas.drawRoundRect(rectF, radius, radius, paint); return result; } @Override public String getId() { return getClass().getName() + Math.round(radius); } }
activity_screen_shot1.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"> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/pic_iv" android:layout_width="match_parent" android:layout_height="wrap_content" android:scaleType="fitXY" android:src="@mipmap/picture" /> <com.lyw.myproject.widget.ScreenshotNotifyView xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/display_screenshot_snv" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone" app:cardBackgroundColor="@color/src_trans" /> </FrameLayout> <Button android:id="@+id/screen_btn" android:text="截图" android:layout_gravity="center" android:layout_marginTop="20dp" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout>
ScreenShotActivity1.java
package com.lyw.myproject.screenshot; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Canvas; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.util.Log; import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.Button; import android.widget.ImageView; import android.widget.Toast; import com.lyw.myproject.BaseActivity; import com.lyw.myproject.R; import com.lyw.myproject.utils.LoadingLayout; import com.lyw.myproject.utils.MemoryUtils; import com.lyw.myproject.utils.PermissionUtil; import com.lyw.myproject.widget.ScreenshotNotifyView; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import androidx.annotation.Nullable; /** * 功能描述:截图 */ public class ScreenShotActivity1 extends BaseActivity { private ImageView picImg; private Button screenBtn; private ScreenshotNotifyView mDisplayScreenshotSnv; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_screen_shot1); initView(); initEvent(); } private void initView() { picImg = (ImageView) findViewById(R.id.pic_iv); mDisplayScreenshotSnv = (ScreenshotNotifyView) findViewById(R.id.display_screenshot_snv); screenBtn = (Button) findViewById(R.id.screen_btn); } private void initEvent() { screenBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { handleScreenShot(); } }); } /** * 处理截屏的业务 */ private void handleScreenShot() { if (!PermissionUtil.isHasSDCardWritePermission(this)) { Toast.makeText(ScreenShotActivity1.this, "没有权限", Toast.LENGTH_SHORT).show(); PermissionUtil.requestSDCardWrite(this); return; } if (!MemoryUtils.hasEnoughMemory(MemoryUtils.MIN_MEMORY)) { Toast.makeText(ScreenShotActivity1.this, "内存不足,截图失败", Toast.LENGTH_SHORT).show(); return; } String filePath = getFilePath(); mDisplayScreenshotSnv.setVisibility(View.GONE); mDisplayScreenshotSnv.setPath(filePath, picImg.getMeasuredWidth(), picImg.getMeasuredHeight(), true); mDisplayScreenshotSnv.setVisibility(View.VISIBLE); } private String getFilePath() { Bitmap bitmap = createViewBitmap(picImg); if (bitmap != null) { try { // 首先保存图片 String storePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "HIS"; File appDir = new File(storePath); if (!appDir.exists()) { appDir.mkdir(); } String fileName = System.currentTimeMillis() + ".jpg"; File file = new File(appDir, fileName); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file)); bitmap.compress(Bitmap.CompressFormat.JPEG, 80, bos); bos.flush(); bos.close(); Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); Uri uri = Uri.fromFile(file); intent.setData(uri); sendBroadcast(intent); return file.getAbsolutePath(); } catch (Exception e) { return null; } } else { return null; } } public Bitmap createViewBitmap(View v) { Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); v.draw(canvas); return bitmap; } }
PermissionUtil.java
package com.lyw.myproject.utils; import android.Manifest; import android.app.Activity; import android.content.Context; import android.content.pm.PackageManager; import android.os.Build; import androidx.core.app.ActivityCompat; public class PermissionUtil { /** * 请求地理位置 * * @param context */ public static void requestLocationPermission(Context context) { if (Build.VERSION.SDK_INT >= 23) { if (!isHasLocationPermission(context)) { ActivityCompat.requestPermissions((Activity) context, PermissionManager.PERMISSION_LOCATION, PermissionManager.REQUEST_LOCATION); } } } /** * 判断是否有地理位置 * * @param context * @return */ public static boolean isHasLocationPermission(Context context) { return ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED; } /** * 判断是否有文件读写的权限 * * @param context * @return */ public static boolean isHasSDCardWritePermission(Context context) { return ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; } /** * 文件权限读写 * * @param context */ public static void requestSDCardWrite(Context context) { if (Build.VERSION.SDK_INT >= 23) { if (!isHasSDCardWritePermission(context)) { ActivityCompat.requestPermissions((Activity) context, PermissionManager.PERMISSION_SD_WRITE, PermissionManager.REQUEST_SD_WRITE); } } } }
MemoryUtils.java
package com.lyw.myproject.utils; import android.util.Log; public class MemoryUtils { public static final int MIN_MEMORY = 50 * 1024 * 1024; /** * 判断有没足够内存截图 * * @param size * @return */ public static boolean hasEnoughMemory(int size) { //最大内存 long maxMemory = Runtime.getRuntime().maxMemory(); //分配的可用内存 long freeMemory = Runtime.getRuntime().freeMemory(); //已用内存 long usedMemory = Runtime.getRuntime().totalMemory() - freeMemory; //剩下可使用的内存 long canUseMemory = maxMemory - usedMemory; Log.d("Memory", "hasEnoughMemory: " + "maxMemory = " + maxMemory + ", freeMemory = " + freeMemory + ", usedMemory = " + usedMemory + ", canUseMemory = " + canUseMemory); if (canUseMemory >= size) { return true; } return false; } }