問題重述
這次的題目有點(diǎn)長,特意的將兩個類似的東西進(jìn)行了劃分。也是為了完全重現(xiàn)我在遇到和解決這兩個問題時候的過程。在一開始,是需求那邊要我做一個我們聊天面板的漸變效果。經(jīng)過多方查證,終于實現(xiàn)了這個功能。后來在迭代版本的時候又要加一個觀眾列表的右側(cè)透明漸變效果,就想到了還用公屏同樣的代碼,然而中間竟然遇到了坑,還找不到攻略(好慌-。-),所以記錄一下,防止后面遇到相同的問題沒有資源參考。
-
先上個效果圖,靜態(tài)滴
兩種效果顯示 你們要的 Git地址,拿去不謝!(●′?`●)
聊天面板的實現(xiàn)
在開始我就是要實現(xiàn)一個聊天面板的消息漸變消失效果(如上圖),經(jīng)過查找資料,最后使用了下面的代碼實現(xiàn):
// 實現(xiàn)漸變效果
Paint mPaint;
private int layerId;
private LinearGradient linearGradient;
public void doTopGradualEffect(){
mPaint = new Paint();
// dst_in 模式,實現(xiàn)底層透明度隨上層透明度進(jìn)行同步顯示
//(即上層為透明時,下層就透明,并不是上層覆蓋下層)
// 具體關(guān)于PorterDuff.Mode的東西大家可以自行查閱了解
final Xfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
mPaint.setXfermode(xfermode);
// 透明位置不變,位于Recyclerview偏上位置
// 使用 CLAMP 模式邊緣拉伸,完美契合背景顏色
linearGradient = new LinearGradient(0.0f, 0.0f, 0.0f, 100.0f, new int[]{0, Color.BLACK}, null, Shader.TileMode.CLAMP);
addItemDecoration(new RecyclerView.ItemDecoration() {
@Override
public void onDrawOver(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(canvas, parent, state);
mPaint.setXfermode(xfermode);
mPaint.setShader(linearGradient);
canvas.drawRect(0.0f, 0.0f, parent.getRight(), 200.0f, mPaint);
mPaint.setXfermode(null);
canvas.restoreToCount(layerId);
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
// 此處 Paint的參數(shù)這里傳的null, 在傳入 mPaint 時會出現(xiàn)
// 第一次打開黑屏閃現(xiàn)的問題
// 注意 saveLayer 不能省也不能移動到onDrawOver方法里
layerId = c.saveLayer(0.0f, 0.0f, (float) parent.getWidth(), (float) parent.getHeight(), null, Canvas.ALL_SAVE_FLAG);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
// 該方法作用自行百度
super.getItemOffsets(outRect, view, parent, state);
}
});
}
注釋還是比較清楚的。主要使用了 Recyclerview 的 addItemDecoration 和 PorterDuff.Mode 的 DST_IN 模式,這個模式下繪制的效果會受到源圖像(即代碼中的 drawRect)透明度的影響,因為我們使用了漸變的 Shader--LinearGradient,所以畫出來的矩形透明度是漸變的,當(dāng)我們 restore 的時候就會影響到底層的透明度(即公屏消息的透明度)。

觀眾列表右側(cè)透明漸變
這里我要強(qiáng)調(diào) 右側(cè),因為我在考慮這個功能時馬上想到了使用上面類似的代碼,然而在左側(cè)是OK的,可怎么也移動不到右側(cè)。開始走了一條不歸路。因為左側(cè)是OK的,所以我想只需要把透明位置移動到右側(cè)就可以了,就想修改 drawRect 的左右邊界線來實現(xiàn)移動,但是都說了是不歸路(╥╯^╰╥),期間進(jìn)行了各種修改調(diào)試,最后甚至嘗試了蓋一張圖片,添加一個毛玻璃蒙層,但效果都不好。在休息了一晚后,我又回到上次的記錄點(diǎn)(左側(cè)OK),終于讓我發(fā)現(xiàn)了問題所在,原來控制透明度的位置不是通過控制矩形的位置,而是通過 linearGradient 的位置來實現(xiàn)的。后面又出現(xiàn)一些小問題,最終出世了以下代碼:
// 實現(xiàn)漸變效果
Paint mPaint;
private int layerId;
private LinearGradient linearGradient;
private int preWidth = 0;// Recyclerview寬度動態(tài)變化時,監(jiān)聽每一次的寬度
public void doTopGradualEffect(){
mPaint = new Paint();
// dst_in 模式,實現(xiàn)底層透明度隨上層透明度進(jìn)行同步顯示(即上層為透明時,下層就透明,并不是上層覆蓋下層)
final Xfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
mPaint.setXfermode(xfermode);
addItemDecoration(new RecyclerView.ItemDecoration() {
@Override
public void onDrawOver(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(canvas, parent, state);
// 當(dāng)linearGradient為空即第一次繪制 或 Recyclerview寬度發(fā)生改變時,
// 重新計算透明位置
if (linearGradient == null || preWidth!=parent.getWidth()){
// 透明位置從最后一個 itemView 的一半處到 Recyclerview 的最右邊
linearGradient = new LinearGradient(parent.getWidth()-(itemViewWidth/2),
0.0f, parent.getWidth(), 0.0f, new int[]{Color.BLACK, 0}, null, Shader.TileMode.CLAMP);
preWidth = parent.getWidth();
}
mPaint.setXfermode(xfermode);
mPaint.setShader(linearGradient);
canvas.drawRect(0.0f, 0.0f, parent.getRight(), parent.getBottom(), mPaint);
mPaint.setXfermode(null);
canvas.restoreToCount(layerId);
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
// 此處 Paint的參數(shù)這里傳的null, 在傳入 mPaint 時會出現(xiàn)第一次打開黑屏閃現(xiàn)的問題
// 注意 saveLayer 不能省也不能移動到onDrawOver方法里
layerId = c.saveLayer(0.0f, 0.0f, (float) parent.getWidth(), (float) parent.getHeight(), null, Canvas.ALL_SAVE_FLAG);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
// 該方法作用自行百度
super.getItemOffsets(outRect, view, parent, state);
}
});
}
苦惱了一天的問題原來就在于一個參數(shù)的修改 (?^?^)?,終于順利完成了這個功能,后來由于我們的需求是少于3個人右對齊,3個人以上左對齊,所以添加了動態(tài)計算的代碼。
- Tip:我示例的代碼都是在我自定義的Recyclerview中,所以是直接調(diào)用的addItemDecoration,還有記得 saveLayer 時如果傳了 Paint 的話可能出現(xiàn)界面打開的時候閃一個黑屏。
轉(zhuǎn)載請聯(lián)系作者--維權(quán)騎士,盜版必究
