Android O 自定義通知例項及一個自定義自動適配縮放圖片至特定大小的田字格ImageView
這一篇博文的思路很簡單,沒有什麼深入的理論,源由是最近在實習公司裡開發用到了Android O的通知,發現與以往有所不同,相關的資料是有,但是大部分都不適用於這個版本,這裡只是寫一個非常簡單的Demo,算是程式碼的儲存吧。另外,為什麼一個自定義View要和通知弄一起說呢,是因為本想在這個通知上用上一個自定義的View,結果發現,通知是不支援自定義View 的。
具體通知的知識點,網上有很多,這裡不再綴述。
Android O的通知與以往最大的不同,在於增加的頻道這個概念,所以,在這個版本,倘若,不加入頻道,以之前的方式去控制通知,就會彈出以下的錯誤(彈出一個Toast :Developer warning for package):
所以和以往的最大不同就在此,其他的設定,具體看一下api測試即可。
說一說自定義通知,自定義通知當然是個建立一個佈局檔案,然後根據佈局檔案載入到通知即可。方法是通過建立一個RemoteViews,呼叫RemoteViews的一系列方法,通過傳入View控制元件的id,及相對應的值,即可完成設定,這個過程也非常簡單。程式碼如下:
Ps:一定要設定setSmallIcon,否則不能顯示。
另外,假若你設定了進度條,然後又看不見效果,請注意有沒有設定其margin值,有些時候是會被其他的通知給蓋住的。至於進度條的樣式,可以通過設定其Style或者Theme標籤達到想到的效果,如:notificationManager=(NotificationManager)getSystemService(NOTIFICATION_SERVICE); NotificationChannel mChannel=new NotificationChannel("my_channel_01","123",NotificationManager.IMPORTANCE_LOW); mChannel.setDescription("123456"); mChannel.enableLights(false); notificationManager.createNotificationChannel(mChannel); builder=new Notification.Builder(this) .setSmallIcon(R.mipmap.ic_launcher); //一定要設定,不然不能夠彈出 RemoteViews remoteViews=new RemoteViews(getPackageName(),R.layout.notification_layout); remoteViews.setTextViewText(R.id.down_tv,"正在下載"); remoteViews.setProgressBar(R.id.pb,100,50,false); builder.setCustomBigContentView(remoteViews); btn_notification=(Button)findViewById(R.id.btn_notification); btn_notification.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { notificationManager.notify(nofitication_id,builder.build()); } });
style="?android:attr/progressBarStyleHorizontal"
本來想在通知上加下一個自定義控制元件,不過一加上去就報了錯誤,查了一下資料,才得知,原來自定義的通過是加不了自定義控制元件的。
這個自定義控制元件的需求是,傳入四張圖片,然後可以根據控制元件的大小去自動調整圖片的顯示大小,同時對圖片的大小進行相對應比例的切割,同時再對圖片進行壓縮,達到記憶體調優的效果。
自定義控制元件好的教程也是非常多,我這裡簡單地敘述一下自己的實戰經歷。
首先,繼承View,然後重寫一個含AttributeSet的方法,這個方法似乎是必須,當然這個還要用到自定義屬性,方法就是在attrs.xml裡進行定義即可,具體看其他教程。這裡獲得使用者傳入的資原始檔:
<com.example.jakera.notificationtest.FourImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:ImageView1="@mipmap/timg"
app:ImageView2="@mipmap/timg1"
app:ImageView3="@mipmap/timg3"
app:ImageView4="@mipmap/timg4"
android:background="#269194"/>
TypedArray ta=context.obtainStyledAttributes(attrs,R.styleable.FourImageView); imageView1=ta.getResourceId(R.styleable.FourImageView_ImageView1,R.drawable.ic_launcher_background);
imageView2=ta.getResourceId(R.styleable.FourImageView_ImageView2,R.drawable.ic_launcher_background);
imageView3=ta.getResourceId(R.styleable.FourImageView_ImageView3,R.drawable.ic_launcher_background);
imageView4=ta.getResourceId(R.styleable.FourImageView_ImageView4,R.drawable.ic_launcher_background);
由於onMeasure方法,我們不需要對View的測量作什麼處理,這裡不重寫。
最重要的當然是onDraw方法,這裡我是直接根據使用者傳入的View大小,計算出每個View的大小,及相對應的間隔。當然,後期應該考慮到可拓展性,應該通過image的個數來計算。
int viewWidth=(getWidth()/51)*25;
int viewWidthSpan=getWidth()/51;
int viewHeight=(getHeight()/51)*25;
int viewHeightSpan=getHeight()/51;
根據上邊的imageview,及View的大小來進行繪製。Rect封裝了View的大小。通過計算能夠把四張圖張按田字格繪製出來。 Rect mDestRect1=new Rect(0,0,viewWidth,viewHeight);
canvas.drawBitmap(getBitmap(imageView1,viewHeight,viewWidth),null,mDestRect1,null);
Rect mDestRect2=new Rect(viewWidth+viewWidthSpan,0,getWidth(),viewHeight);
canvas.drawBitmap(getBitmap(imageView2,viewHeight,viewWidth),null,mDestRect2,null);
Rect mDestRect3=new Rect(0,viewHeight+viewHeightSpan,viewWidth,getHeight());
canvas.drawBitmap(getBitmap(imageView3,viewHeight,viewWidth),null,mDestRect3,null);
Rect mDestRect4=new Rect(viewWidth+viewWidthSpan,viewHeight+viewHeightSpan,getWidth(),getHeight());
canvas.drawBitmap(getBitmap(imageView4,viewHeight,viewWidth),null,mDestRect4,null);
上邊的程式碼呼叫一個方法,這個方法是作用是,由於我們傳入的圖片形狀不一,假若不進行處理,會出現一個問題,就是圖片會在某個方向上被拉伸,顯得特別不友好。另一個問題是,我們要把圖片放到一個小小的View裡邊,應該做一下壓縮處理,不用把大圖載入到記憶體去。
一開始圖方便,在網上找了許多的程式碼段,發現很多程式碼都不適用,有些可以用,但是可能在某些情況下,還是存在拉伸問題,比如當View的寬>長時,可以使用,但是反過來長>寬時不行,搗鼓了大半天,還是沒能弄好,索性自己耐下心下來做分析計算,結果還花不了半個小時就完成了。其實也簡單,就是一些簡單的數學計算。
思路,首先,根據View要顯示圖片大小的比例,去切割自己的圖片,切割自己的圖片要求:按比例切,然後把這個最大的切割區域移動圖片中心,即得到圖片的中心位置。
下圖為當W/r<h的情況,即切割不會超出圖片邊界,則寬度保持不變,而高度截出nh的高度,居中也很簡單,由於寬度不變,則水平不需偏移,則rX=0;而y的偏移,則為(h-需要截出的高度再除以2)即可。這樣便可以截出。
l表示想要設定view的寬度,s表示想要設定View的高度,w即為圖片寬度,h圖片高度rX 水平方向的偏移量,rY垂直方向偏移量。r為寬高比。nh圖片切割後的高度,nw圖片切割後的寬度。
同理,還有一種情況是,當w除以比率得到的新高超出圖片高度時,即得換得高度去乘以比率來獲得新的寬度。分析圖如下:
好的,整個分析就這樣,程式碼如下:
public Bitmap getBitmap(int path,int height,int width){
Bitmap srcBitmap=BitmapFactory.decodeResource(getResources(),path);
int w = srcBitmap.getWidth(); // 得到圖片的寬,高
int h = srcBitmap.getHeight();
int retX, retY;
int nw, nh;
if (w > h * width / height)
{
nh = h;
nw = h * width / height;
retY = 0;
retX = (w - nw) / 2;
} else
{
nh = w * height / width;
nw = w;
retY = (h - nh) / 2;
retX = 0;
}
//在這裡切割圖片
Bitmap bmp = Bitmap.createBitmap(srcBitmap, retX, retY, nw, nh, null,
false);
// 如果是放大圖片,filter決定是否平滑,如果是縮小圖片,filter無影響,我們這裡是縮小圖片,所以直接設定為false
//這裡壓縮圖片至相應大小。
Bitmap dst = Bitmap.createScaledBitmap(bmp, width, height, false);
if (srcBitmap != dst) { // 如果沒有縮放,那麼不回收
srcBitmap.recycle(); // 釋放Bitmap的native畫素陣列
}
bmp.recycle(); //記得回收
srcBitmap.recycle();
return dst;
}
測試控制元件的適配效能,原圖兩張長>寬,兩張寬>長:小圖:
大圖:
高>寬:
寬>高:
好了,一個自定義的通知,以及一個自定義的田字格顯示圖片的View製作而成,當然後期可以再拓展開發,如QQ的九宮格顯示等等。
這個通知也好,控制元件也罷,都挺簡單的,但是真正去寫它,還是花了一些時間,特別是在處理圖片這一塊。另外,我現在有了一個小小的心得,遇到問題,不要一味地去依賴網上的答案,靜下心來想一想,其實自己的解決方案說不定更贊。
加油,共勉。