您好,登錄后才能下訂單哦!
小米系統自帶的長截屏應該很多人都用過,效果不錯。當長截屏時listview就會自動滾動,當按下停止截屏時,就會得到一張完整的截屏。
該篇就介紹一下長截屏的原理
上篇中介紹了android屏幕共享實現方式,該篇的原理和上一篇基本一致。
獲取view影像
當我們想得到一個view的影像時,我們可以調用系統api,得到view的bitmap,但有時可能得不到。我們可以通過另一種方式得到。
首先創建一個和view一樣大小的bitmap
然后把view繪制到bmp上
Canvas canvas = new Canvas(); canvas.setBitmap(bmp); view.draw(canvas);
執行完上面代碼后bmp上就是view的影像了。
制造滾動事件,促使view滾動
我們可以創建一個MotionEvent,然后定時修改MotionEvent的y值,并分發給view,從而促使view上下滾動。當然我們也可以定時修改x值促使view左右滾動。
代碼大致如下
final MotionEvent motionEvent = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, view.getWidth() / 2, view.getHeight() / 2, 0); view.postDelayed(new Runnable() { @Override public void run() { motionEvent.setAction(MotionEvent.ACTION_MOVE); motionEvent.setLocation((int) motionEvent.getX(), (int) motionEvent.getY() - 1); //把事件分發給view view.dispatchTouchEvent(motionEvent); view.postDelayed(this, DELAY); } }, DELAY);
注意:從分發DOWN事件到結束都要使用同一個MotionEvent對象,只需要不斷改變x或y值。
每次x或y的值相對于上次改動不能過大,若過大,view實際滾動距離可能達不到為MotionEvent設置的值(因view滾動時卡頓導致)。
截屏
當為MotionEvent設置的x或y值正好時當前view的大小時,創建新的bitmap,通過上述方法把view繪制到bitmap上,想要停止截屏時拼接所有bitmap即可。
備注
當我們想要把Listview長截屏時,需要為ListView外面嵌套一層和ListView一樣大小的View,以上的所有操作都在嵌套的這層view上操作。當我們調用嵌套的這層view的draw(new Canvas(bmp))時會把當前看到的這塊ListView繪制到bmp上,不管ListView嵌套了多少層子view都可以繪制到當前bmp上。
由于ListView中根據滑動的距離是否大于ViewConfiguration.get(view.getContext()).getScaledTouchSlop() )來確定要不要滾動,所以一開始我們要特殊處理下,為什么是ViewConfiguration.get(view.getContext()).getScaledTouchSlop() )可以查看ListView的事件分發相關函數得到(dispatchTouchEvent),讓Listview認為是開始滾動,這樣才能保證以后分發的滑動距離和實際滾動距離一致。
Listview也要通知是否滾動到了最后,不然如果沒有手動停止的話,雖然還是在一直分發滾動事件,但ListView不再滾動,導致最終截圖后后面全是重復的最后一屏幕。
附 實現大致方式代碼,有待優化
package com.example.wanjian.test; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.os.Environment; import android.os.SystemClock; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.widget.LinearLayout; import android.widget.Toast; import java.io.File; import java.io.FileOutputStream; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; /** * Created by wanjian on 16/8/18. */ public class ScrollableViewRECUtil { public static final int VERTICAL = 0; private static final int DELAY = 2; private List<Bitmap> bitmaps = new ArrayList<>(); private int orientation = VERTICAL; private View view; private boolean isEnd; private OnRecFinishedListener listener; public ScrollableViewRECUtil(View view, int orientation) { this.view = view; this.orientation = orientation; } public void start(final OnRecFinishedListener listener) { this.listener = listener; final MotionEvent motionEvent = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, view.getWidth() / 2, view.getHeight() / 2, 0); view.dispatchTouchEvent(motionEvent); motionEvent.setAction(MotionEvent.ACTION_MOVE); //滑動距離大于ViewConfiguration.get(view.getContext()).getScaledTouchSlop()時listview才開始滾動 motionEvent.setLocation(motionEvent.getX(), motionEvent.getY() - (ViewConfiguration.get(view.getContext()).getScaledTouchSlop() + 1)); view.dispatchTouchEvent(motionEvent); motionEvent.setLocation(motionEvent.getX(), view.getHeight() / 2); view.postDelayed(new Runnable() { @Override public void run() { if (isEnd) { //停止時正好一屏則全部繪制,否則繪制部分 if ((view.getHeight() / 2 - (int) motionEvent.getY()) % view.getHeight() == 0) { Bitmap bitmap = rec(); bitmaps.add(bitmap); } else { Bitmap origBitmap = rec(); int y = view.getHeight() / 2 - (int) motionEvent.getY(); Bitmap bitmap = Bitmap.createBitmap(origBitmap, 0, view.getHeight() - y % view.getHeight(), view.getWidth(), y % view.getHeight()); bitmaps.add(bitmap); origBitmap.recycle(); } //最后一張可能高度不足view的高度 int h = view.getHeight() * (bitmaps.size() - 1); Bitmap bitmap = bitmaps.get(bitmaps.size() - 1); h = h + bitmap.getHeight(); Bitmap result = Bitmap.createBitmap(view.getWidth(), h, Bitmap.Config.RGB_565); Canvas canvas = new Canvas(); canvas.setBitmap(result); for (int i = 0; i < bitmaps.size(); i++) { Bitmap b = bitmaps.get(i); canvas.drawBitmap(b, 0, i * view.getHeight(), null); b.recycle(); } listener.onRecFinish(result); return; } if ((view.getHeight() / 2 - (int) motionEvent.getY()) % view.getHeight() == 0) { Bitmap bitmap = rec(); bitmaps.add(bitmap); } motionEvent.setAction(MotionEvent.ACTION_MOVE); //模擬每次向上滑動一個像素,這樣可能導致滾動特別慢,實際使用時可以修改該值,但判斷是否正好滾動了 //一屏幕就不能簡單的根據 (view.getHeight() / 2 - (int) motionEvent.getY()) % view.getHeight() == 0 來確定了。 //可以每次滾動n個像素,當發現下次再滾動n像素時就超出一屏幕時可以改變n的值,保證下次滾動后正好是一屏幕, //這樣就可以根據(view.getHeight() / 2 - (int) motionEvent.getY()) % view.getHeight() == 0來判斷要不要截屏了。 motionEvent.setLocation((int) motionEvent.getX(), (int) motionEvent.getY() - 1); view.dispatchTouchEvent(motionEvent); view.postDelayed(this, DELAY); } }, DELAY); } public void stop() { isEnd = true; } private Bitmap rec() { Bitmap film = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.RGB_565); Canvas canvas = new Canvas(); canvas.setBitmap(film); view.draw(canvas); return film; } public interface OnRecFinishedListener { void onRecFinish(Bitmap bitmap); } }
activity代碼
setContentView(R.layout.activity_main4); // listview= (ListView) findViewById(R.id.listview); listview.setAdapter(new BaseAdapter() { @Override public int getCount() { return 100; } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView==null){ Button button= (Button) LayoutInflater.from(getApplication()).inflate(R.layout.item,listview,false); button.setText(""+position); return button; } ((Button)convertView).setText(""+position); return convertView; } }); // File file=new File(Environment.getExternalStorageDirectory(),"aaa"); file.mkdirs(); for (File f:file.listFiles()){ f.delete(); } listview.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { listview.getViewTreeObserver().removeGlobalOnLayoutListener(this); start(); } });
private void start(){ final View view=findViewById(R.id.view); final ScrollableViewRECUtil scrollableViewRECUtil=new ScrollableViewRECUtil(view,ScrollableViewRECUtil.VERTICAL); scrollableViewRECUtil.start(new ScrollableViewRECUtil.OnRecFinishedListener() { @Override public void onRecFinish(Bitmap bitmap) { File f= Environment.getExternalStorageDirectory(); System.out.print(f.getAbsoluteFile().toString()); Toast.makeText(getApplicationContext(),f.getAbsolutePath(),Toast.LENGTH_LONG).show(); try { bitmap.compress(Bitmap.CompressFormat.JPEG,60,new FileOutputStream(new File(f,"rec"+System.currentTimeMillis()+".jpg"))); Toast.makeText(getApplicationContext(),"Success",Toast.LENGTH_LONG).show(); }catch (Exception e){ e.printStackTrace(); } } }); // scrollableViewRECUtil view.postDelayed(new Runnable() { @Override public void run() { scrollableViewRECUtil.stop(); } },90*1000); }
布局
<?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:id="@+id/view" android:orientation="vertical" > <ListView android:id="@+id/listview" android:layout_width="match_parent" android:layout_height="match_parent" android:divider="#e1e1e1" android:dividerHeight="2dp" ></ListView> </LinearLayout>
效果圖
屏幕
最終截屏
可以看到毫無拼接痕跡。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。