模仿某東商城的詳情頁(yè)面截屏?xí)袕棿笆录捻憫?yīng)功能大體實(shí)現(xiàn)了,細(xì)節(jié)沒(méi)做請(qǐng)不要糾結(jié),進(jìn)入正題瓤荔。
對(duì)于系統(tǒng)級(jí)別截屏的監(jiān)聽(tīng),api中并沒(méi)有針對(duì)于這個(gè)功能的監(jiān)聽(tīng)回調(diào)等方法洞就。我的首先想到的就是針對(duì)資源文件的監(jiān)聽(tīng)擅羞。
思路如下:
1.對(duì)于系統(tǒng)級(jí)別的截圖 肯定會(huì)保存到本地(機(jī)型不一樣要注意適配轴脐,主要還是截圖的命名方式不太一樣)
2.對(duì)于資源文件監(jiān)聽(tīng)時(shí)要判斷是否是符合截圖要求的(主要還是截圖時(shí)間與當(dāng)時(shí)時(shí)間差不能太大堪藐,否則會(huì)因?yàn)橛脩?hù)的其他操作影響到)
3.當(dāng)檢測(cè)到資源文件有變化查詢(xún)最后一條并返回展示
話(huà)不多說(shuō)上代碼
MainActivity
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import java.io.File;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private ImageView img;
private ScreenShot mScreenShot;
private TextView tvHelp;
private TextView tvShare;
private PopupWindow popupWindow;
private LinearLayout linearLayout;
private View showView;
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
}
private void initView() {
linearLayout = findViewById(R.id.linearLayout);
showView = LayoutInflater.from(this).inflate(R.layout.layout_show_creenshot, null);
popupWindow = new PopupWindow(showView,
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
img = showView.findViewById(R.id.show_img);
tvHelp = showView.findViewById(R.id.tv_help);
tvShare = showView.findViewById(R.id.tv_share);
tvHelp.setOnClickListener(this);
tvShare.setOnClickListener(this);
popupWindow.setWidth(300);
popupWindow.setHeight(500);
}
private void initData() {
mScreenShot = ScreenShot.getInstance();
handelSystemScreenShot();
}
private void handelSystemScreenShot() {
if (!MainActivity.this.isFinishing()) {
mScreenShot.register(MainActivity.this, new ScreenShot.CallbackListener() {
@Override
public void onShot(String path) {
final String pa = path;
Log.i("tag", "onShot: 成功:" + path);
runOnUiThread(new Runnable() {
@Override
public void run() {
img.setImageURI(Uri.fromFile(new File(pa)));
popupWindow.showAtLocation(linearLayout, Gravity.LEFT,0,0);
}
});
}
});
} else {
mScreenShot.unregister();
}
}
@Override
public void onClick(View v) {
if(v.getId()==R.id.tv_help){
Toast.makeText(MainActivity.this,"客服",Toast.LENGTH_LONG).show();
}else {
Toast.makeText(MainActivity.this,"分享",Toast.LENGTH_LONG).show();
}
}
@Override
protected void onDestroy() {
mScreenShot.unregister();
super.onDestroy();
}
}
main布局
<?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/linearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="80dp"
android:background="@mipmap/ic_launcher"
/>
</LinearLayout>
ScreenShot 類(lèi)
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.provider.MediaStore;
/**
* 系統(tǒng)截屏監(jiān)聽(tīng)工具滋恬,監(jiān)聽(tīng)系統(tǒng)截屏匀哄,然后對(duì)截圖進(jìn)行處理
*/
public class ScreenShot {
private static final String TAG = "ScreenShot";
private static final String[] MEDIA_PROJECTIONS = {
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.DATE_ADDED
};
/**
* 截屏依據(jù)中的路徑判斷關(guān)鍵字
*/
private static final String[] KEYWORDS = {
"screenshot", "screen_shot", "screen-shot", "screen shot", "screencapture",
"screen_capture", "screen-capture", "screen capture", "screencap", "screen_cap",
"screen-cap", "screen cap"
};
private ContentResolver mContentResolver;
private CallbackListener mCallbackListener;
private MediaContentObserver mInternalObserver;
private MediaContentObserver mExternalObserver;
private static ScreenShot mInstance;
private ScreenShot() {
}
/**
* 獲取 ScreenShot 對(duì)象
*
* @return ScreenShot對(duì)象
*/
public static ScreenShot getInstance() {
if (mInstance == null) {
synchronized (ScreenShot.class) {
mInstance = new ScreenShot();
}
}
return mInstance;
}
/**
* 注冊(cè)
*
* @param context 上下文
* @param callbackListener 回調(diào)監(jiān)聽(tīng)
*/
public void register(Context context, CallbackListener callbackListener) {
mContentResolver = context.getContentResolver();
mCallbackListener = callbackListener;
HandlerThread handlerThread = new HandlerThread(TAG);
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
mInternalObserver = new MediaContentObserver(MediaStore.Images.Media.INTERNAL_CONTENT_URI, handler);
mExternalObserver = new MediaContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, handler);
mContentResolver.registerContentObserver(MediaStore.Images.Media.INTERNAL_CONTENT_URI,
true, mInternalObserver);
mContentResolver.registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
true, mExternalObserver);
}
/**
* 注銷(xiāo)
*/
public void unregister() {
if (mContentResolver != null) {
mContentResolver.unregisterContentObserver(mInternalObserver);
mContentResolver.unregisterContentObserver(mExternalObserver);
}
}
private void handleMediaContentChange(Uri uri) {
Cursor cursor = null;
try {
// 數(shù)據(jù)改變時(shí)秦效,查詢(xún)數(shù)據(jù)庫(kù)中最后加入的一條數(shù)據(jù)
cursor = mContentResolver.query(uri, MEDIA_PROJECTIONS, null, null,
MediaStore.Images.ImageColumns.DATE_ADDED + " desc limit 1");
if (cursor == null) {
return;
}
if (!cursor.moveToFirst()) {
return;
}
int dataIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
// 處理獲取到的第一行數(shù)據(jù)
handleMediaRowData(cursor.getString(dataIndex));
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
}
}
/**
* 處理監(jiān)聽(tīng)到的資源
*/
private void handleMediaRowData(String path) {
long duration = 0;
long step = 100;
// 設(shè)置最大等待時(shí)間為1s雏蛮,因?yàn)轺茸宓牟糠质謾C(jī)保存截圖有延遲
while (!checkScreenShot(path) && duration <= 1000) {
try {
duration += step;
Thread.sleep(step);
} catch (Exception e) {
e.printStackTrace();
}
}
if (checkScreenShot(path)) {
if (mCallbackListener != null) {
//回調(diào)地址
mCallbackListener.onShot(path);
}
}
}
private boolean checkScreenShot(String path) {
if (path == null) {
return false;
}
path = path.toLowerCase();
for (String keyword : KEYWORDS) {
//判斷是否符合 截圖命名規(guī)則(過(guò)濾)
if (path.contains(keyword)) {
return true;
}
}
return false;
}
/**
* 媒體內(nèi)容觀(guān)察者
*/
private class MediaContentObserver extends ContentObserver {
private Uri mUri;
MediaContentObserver(Uri uri, Handler handler) {
super(handler);
mUri = uri;
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
//Log.d("ScreenShot", "圖片數(shù)據(jù)庫(kù)發(fā)生變化:" + selfChange);
handleMediaContentChange(mUri);
}
}
/**
* 回調(diào)監(jiān)聽(tīng)器
*/
public interface CallbackListener {
/**
* 截屏
*
* @param path 圖片路徑
*/
void onShot(String path);
}
}
彈窗
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:background="@color/colorBlack"
android:orientation="vertical">
<ImageView
android:id="@+id/show_img"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/tv_share"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorBlack"
android:gravity="center"
android:text="分享頁(yè)面"
android:textColor="@color/colorWrite" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/colorWrite" />
<TextView
android:id="@+id/tv_help"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorBlack"
android:gravity="center"
android:text="問(wèn)問(wèn)客服"
android:textColor="@color/colorWrite" />
</LinearLayout>
</RelativeLayout>
關(guān)于這個(gè)還有幾個(gè)點(diǎn)需要注意
由于是對(duì)系統(tǒng)資源變化的監(jiān)聽(tīng) 所以會(huì)導(dǎo)致以下的問(wèn)題
1.由于機(jī)型 廠(chǎng)商 版本等問(wèn)題影響 會(huì)導(dǎo)致截屏圖片 存儲(chǔ)圖片消耗的時(shí)間不同
2.刪除圖片資源時(shí) 也可能會(huì)調(diào)用 回調(diào)接口
解決:查詢(xún)圖片時(shí)也對(duì)圖片的時(shí)間進(jìn)行判斷 目前暫定3秒(除人為極限操作)能夠滿(mǎn)足圖片插入時(shí)間和刪除回調(diào)的時(shí)間判斷
系統(tǒng)在程序外截圖也會(huì)通過(guò)過(guò)濾被程序檢測(cè)到
1.添加過(guò)濾 因?yàn)橄到y(tǒng)截圖時(shí)會(huì)自動(dòng)帶有截屏?xí)r間 截屏程序 以及存放位置 增加對(duì)于截屏程序包名過(guò)濾
已經(jīng)找到和某東詳情截圖相同的方法了 已更新
碼字不易,點(diǎn)贊收藏謝謝!!!