原因:自己公司项目有IM功,基于万物皆克抄原则,还原微信你可发送的图片功能
要求获取的图片:
1:相机图片
2:截图图片
3:下载图片
4:三方截图,三方相机照片
思路:
1:获取相机文件夹下图片以及照片
1:获取相机路径(魅族特殊)
String CAMERA_IMAGE_BUCKET_NAME = "";
if (DeviceUtils.isMeizu()) {
//魅族拍照图片直接放在DCIM中
CAMERA_IMAGE_BUCKET_NAME =
Environment.getExternalStorageDirectory().toString() +"/DCIM";
} else {
CAMERA_IMAGE_BUCKET_NAME = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera";
}
2:获取截图路径
String path = Environment.getExternalStorageDirectory().toString() + "/DCIM/Screenshots";
File file = new File(path);
if (!file.exists()) {
path = Environment.getExternalStorageDirectory().toString() + "/Pictures/Screenshots";
}
2:通过游标获取手机资源文件的图片(选用)
注意:
1.游标获取手机最新一张图片更新(限制30秒内)
- MediaColumns.DATE_ADDED 时间格式问题是秒操作的所以查询的时候也得是秒
源码:
package com.android.systemui.screenshot;
class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
...
@Override
protected Void doInBackground(Void... paramsUnused) {
if (isCancelled()) {
return null;
}
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
ContentResolver resolver = mContext.getContentResolver();
Bitmap image = mParams.image;
Resources r = mContext.getResources();
try {
// Save the screenshot to the MediaStore
final ContentValues values = new ContentValues();
values.put(MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES
+ File.separator + Environment.DIRECTORY_SCREENSHOTS);
values.put(MediaColumns.DISPLAY_NAME, mImageFileName);
values.put(MediaColumns.MIME_TYPE, "image/png");
// 注意时间戳存储的是秒
values.put(MediaColumns.DATE_ADDED, mImageTime / 1000);
// 注意时间戳存储的是秒更新时间也是秒
values.put(MediaColumns.DATE_MODIFIED, mImageTime / 1000);
values.put(MediaColumns.DATE_EXPIRES, (mImageTime + DateUtils.DAY_IN_MILLIS) / 1000);
values.put(MediaColumns.IS_PENDING, 1);
final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
CompletableFuture<List<Notification.Action>> smartActionsFuture =
ScreenshotSmartActions.getSmartActionsFuture(
mScreenshotId, uri, image, mSmartActionsProvider,
mSmartActionsEnabled, getUserHandle(mContext));
....
}
}
//你想发送图片时间限制 s
public static final long SCREEN_SHOT_OFFET_TIME_S = 30;
//过滤文件大小防止崩溃
public static final int IMAGE_MAX_SIZE = 25 * 1024 * 1024;
/**
* 获取一定时间内更新的图片( 项目中使用这个)
* @param context
* @return
*/
public static Pair<Long, String> getLatestMediaPhoto(Context context) {
//查询路径和修改时间
String[] projection = {MediaStore.Images.Media.DATA,MediaStore.Images.Media.DATE_MODIFIED};
// SCREEN_SHOT_OFFET_TIME_S 表示查询30秒以内的的新插入的图片
// /1000 的原因是:
// SaveImageInBackgroundTask -> doInBackground()
// -> values.put(MediaColumns.DATE_ADDED, mImageTime / 1000);
//存储截图的时候存储的时间是 秒 所以需要/1000 变成秒
long currentTime = System.currentTimeMillis()/ 1000 - SCREEN_SHOT_OFFET_TIME_S;
//检查camera文件夹,查询并排序
Pair<Long, String> mediaPair = null;
Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection,
MediaStore.Images.Media.DATE_ADDED + " >= ?",
new String[]{currentTime + ""},
MediaStore.Images.Media.DATE_ADDED + " DESC");
if (cursor.moveToFirst()) {
String path= cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
if (path!=null&& FileUtils.isFileExists(path)&& FileUtils.getFileLength(path)<= IMAGE_MAX_SIZE){
//填充更新时间 以及 地址
//注意 cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media.DATE_MODIFIED)) 有可能是秒有可能是毫秒
//获取的时候做判断的时候需要注意
mediaPair = new Pair(cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media.DATE_MODIFIED)),path);
}
}
//对比
if (mediaPair != null ) {
return mediaPair;
}
return null;
}
实现
1.气泡布局
1.1 attrs.xml
<resources>
<declare-styleable name="BubbleLayout">
<attr name="background_color" format="color" />
<attr name="shadow_color" format="color" />
<attr name="shadow_size" format="dimension" />
<attr name="radius" format="dimension" />
<attr name="direction" format="enum">
<enum name="left" value="1" />
<enum name="top" value="2" />
<enum name="right" value="3" />
<enum name="bottom" value="4" />
</attr>
<attr name="offset" format="dimension" />
</declare-styleable>
</resources>
1.2页面
package com.cnlive.strike.moudle.conversation.ui.widget;
/**
* @author wkq
* @date 2021年08月23日 13:09
* @des
*/
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.FrameLayout;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.cnlive.strike.moudle.conversation.R;
public class BubbleLayout extends FrameLayout {
public static final int LEFT = 1;
public static final int TOP = 2;
public static final int RIGHT = 3;
public static final int BOTTOM = 4;
@IntDef({LEFT, TOP, RIGHT, BOTTOM})
private @interface Direction {
}
/**
* 圆角大小
*/
private int mRadius;
/**
* 三角形的方向
*/
@Direction
private int mDirection;
/**
* 三角形的底边中心点
*/
private Point mDatumPoint;
/**
* 三角形位置偏移量(默认居中)
*/
private int mOffset;
private Paint mBorderPaint;
private Path mPath;
private RectF mRect;
public BubbleLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.BubbleLayout);
//背景颜色
int backGroundColor = ta.getColor(R.styleable.BubbleLayout_background_color, Color.WHITE);
//阴影颜色
int shadowColor = ta.getColor(R.styleable.BubbleLayout_shadow_color,
Color.parseColor("#999999"));
int defShadowSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
4, getResources().getDisplayMetrics());
//阴影尺寸
int shadowSize = ta.getDimensionPixelSize(R.styleable.BubbleLayout_shadow_size, defShadowSize);
mRadius = ta.getDimensionPixelSize(R.styleable.BubbleLayout_radius, 0);
//三角形方向
mDirection = ta.getInt(R.styleable.BubbleLayout_direction, BOTTOM);
mOffset = ta.getDimensionPixelOffset(R.styleable.BubbleLayout_offset, 0);
ta.recycle();
mBorderPaint = new Paint();
mBorderPaint.setAntiAlias(true);
mBorderPaint.setColor(backGroundColor);
mBorderPaint.setStrokeWidth(shadowSize);
mPath = new Path();
mRect = new RectF();
mDatumPoint = new Point();
setWillNotDraw(false);
//关闭硬件加速
setLayerType(LAYER_TYPE_SOFTWARE, null);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mDatumPoint.x > 0 && mDatumPoint.y > 0)
switch (mDirection) {
case LEFT:
drawLeftTriangle(canvas);
break;
case TOP:
drawTopTriangle(canvas);
break;
case RIGHT:
drawRightTriangle(canvas);
break;
case BOTTOM:
drawBottomTriangle(canvas);
break;
}
}
private void drawLeftTriangle(Canvas canvas) {
int triangularLength = getPaddingLeft();
if (triangularLength == 0) {
return;
}
mPath.addRoundRect(mRect, mRadius, mRadius, Path.Direction.CCW);
mPath.moveTo(mDatumPoint.x, mDatumPoint.y - triangularLength / 2);
mPath.lineTo(mDatumPoint.x - triangularLength / 2, mDatumPoint.y);
mPath.lineTo(mDatumPoint.x, mDatumPoint.y + triangularLength / 2);
mPath.close();
canvas.drawPath(mPath, mBorderPaint);
}
private void drawTopTriangle(Canvas canvas) {
int triangularLength = getPaddingTop();
if (triangularLength == 0) {
return;
}
mPath.addRoundRect(mRect, mRadius, mRadius, Path.Direction.CCW);
mPath.moveTo(mDatumPoint.x + triangularLength / 2, mDatumPoint.y);
mPath.lineTo(mDatumPoint.x, mDatumPoint.y - triangularLength / 2);
mPath.lineTo(mDatumPoint.x - triangularLength / 2, mDatumPoint.y);
mPath.close();
canvas.drawPath(mPath, mBorderPaint);
}
private void drawRightTriangle(Canvas canvas) {
int triangularLength = getPaddingRight();
if (triangularLength == 0) {
return;
}
mPath.addRoundRect(mRect, mRadius, mRadius, Path.Direction.CCW);
mPath.moveTo(mDatumPoint.x, mDatumPoint.y - triangularLength / 2);
mPath.lineTo(mDatumPoint.x + triangularLength / 2, mDatumPoint.y);
mPath.lineTo(mDatumPoint.x, mDatumPoint.y + triangularLength / 2);
mPath.close();
canvas.drawPath(mPath, mBorderPaint);
}
private void drawBottomTriangle(Canvas canvas) {
int triangularLength = getPaddingBottom();
if (triangularLength == 0) {
return;
}
mPath.addRoundRect(mRect, mRadius, mRadius, Path.Direction.CCW);
mPath.moveTo(mDatumPoint.x + triangularLength / 2, mDatumPoint.y);
mPath.lineTo(mDatumPoint.x, mDatumPoint.y + triangularLength / 2);
mPath.lineTo(mDatumPoint.x - triangularLength / 2, mDatumPoint.y);
mPath.close();
canvas.drawPath(mPath, mBorderPaint);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mRect.left = getPaddingLeft();
mRect.top = getPaddingTop();
mRect.right = w - getPaddingRight();
mRect.bottom = h - getPaddingBottom();
switch (mDirection) {
case LEFT:
mDatumPoint.x = getPaddingLeft();
mDatumPoint.y = h / 2;
break;
case TOP:
mDatumPoint.x = w / 2;
mDatumPoint.y = getPaddingTop();
break;
case RIGHT:
mDatumPoint.x = w - getPaddingRight();
mDatumPoint.y = h / 2;
break;
case BOTTOM:
mDatumPoint.x = w / 2;
mDatumPoint.y = h - getPaddingBottom();
break;
}
if (mOffset != 0) {
applyOffset();
}
}
/**
* 设置三角形偏移位置
*
* @param offset 偏移量
*/
public void setTriangleOffset(int offset) {
this.mOffset = offset;
applyOffset();
invalidate();
}
private void applyOffset() {
switch (mDirection) {
case LEFT:
case RIGHT:
mDatumPoint.y += mOffset;
break;
case TOP:
case BOTTOM:
mDatumPoint.x += mOffset;
break;
}
}
}
2.布局
<BubbleLayout
android:id="@+id/bl_screen_shot"
android:layout_width="100dp"
android:layout_height="160dp"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:paddingRight="5dp"
android:paddingBottom="20dp"
android:visibility="gone"
app:background_color="@color/white"
app:direction="bottom"
app:offset="25dp"
app:radius="4dp"
app:shadow_color="@color/color_7b7b7b"
app:shadow_size="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="你可能要发送的图片:"
android:textColor="@color/color_7b7b7b"
android:textSize="14dp" />
<ImageView
android:id="@+id/iv_screen_shot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp"
android:scaleType="centerCrop" />
</LinearLayout>
</BubbleLayout>
3.调用
/**
* 展示截图
*/
private void showScreenShot() {
//展示过的截图
String oldScreenShotFilePath = SharedPreferencesHelper.getInstance(getContext()).getValue(SCREEN_SHOT);
Pair<Long, String> p = ScreenShotUtil.getLatestMediaPhoto(mContext);
if (p == null || TextUtils.isEmpty(p.second) || !FileUtils.isFileExists(p.second) || oldScreenShotFilePath.equals(p.second))
return;
if (p.first >= 1000000000000l) {
//毫秒
SharedPreferencesHelper.getInstance(getContext()).getValue(SCREEN_SHOT);
if (System.currentTimeMillis() - p.first <= Constants.SCREEN_SHOT_OFFET_TIME_MS) {
showScreenShot(p.second);
}
} else {
//秒
if (System.currentTimeMillis() / 1000 - p.first <= Constants.SCREEN_SHOT_OFFET_TIME_S) {
showScreenShot(p.second);
}
}
}
//展示图片
public void showScreenShot(String filePath) {
if (!TextUtils.isEmpty(filePath) && FileUtils.isFileExists(filePath)) {
screenShotpath = filePath;
Glide.with(mContext).load(screenShotpath).into(ivScreenShot);
blScreenShot.setVisibility(VISIBLE);
SharedPreferencesHelper.getInstance(mContext).setValue(SCREEN_SHOT, filePath);
//开启倒计时隐藏
startTimerTask();
}
}
private void startTimerTask() {
Message message = mHandler.obtainMessage();
message.arg1 = 101;
mHandler.sendMessageDelayed(message, 10 * 1000);
}
private void cancleTimer() {
if (mHandler != null) {
mHandler.removeCallbacksAndMessages(null);
}
}
//展示截图的倒计时
Handler mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.arg1 == 101) {
if (blScreenShot != null) blScreenShot.setVisibility(GONE);
}
}
};
总结
简单贴出来了获取最近图片的代码实现,其中需要注意的是因为源码中图片存储的时候时间单位是秒,所以查询的时候时间也得是秒,然后获取的更新时间有可能是秒也有可能是毫秒注意区分.