問題重述
這次的題目有點長,特意的將兩個類似的東西進行了劃分。也是為了完全重現(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)底層透明度隨上層透明度進行同步顯示
//(即上層為透明時,下層就透明粟判,并不是上層覆蓋下層)
// 具體關(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递礼,所以畫出來的矩形透明度是漸變的,當我們 restore 的時候就會影響到底層的透明度(即公屏消息的透明度)羹幸。
觀眾列表右側(cè)透明漸變
這里我要強調(diào) 右側(cè)脊髓,因為我在考慮這個功能時馬上想到了使用上面類似的代碼,然而在左側(cè)是OK的栅受,可怎么也移動不到右側(cè)将硝。開始走了一條不歸路恭朗。因為左側(cè)是OK的,所以我想只需要把透明位置移動到右側(cè)就可以了依疼,就想修改 drawRect 的左右邊界線來實現(xiàn)移動痰腮,但是都說了是不歸路(╥╯^╰╥),期間進行了各種修改調(diào)試律罢,最后甚至嘗試了蓋一張圖片膀值,添加一個毛玻璃蒙層,但效果都不好误辑。在休息了一晚后沧踏,我又回到上次的記錄點(左側(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)底層透明度隨上層透明度進行同步顯示(即上層為透明時师骗,下層就透明历等,并不是上層覆蓋下層)
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);
// 當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)騎士昵济,盜版必究