仿支付寶錢包:帶分割線的GridView
阿新 • • 發佈:2019-01-04
需求:
本文記錄了我嘗試實現支付寶錢包樣式帶分割線GridView的過程。首先看一下高大上的支付寶錢包首頁:
這裡畫紅框的部分,給人的直觀感覺就是一個GridView 。當然,這裡很可能是支付寶同學自定義了一個GridView的子類 。相對於ListView的單列列表,GridView則是一種支援多行多列的網格形式。 當我們想要實現例如九宮格的效果時,GridView即是首選。
然而,與ListView可以方便的設定和自定義分割線不同不同,GridView貌似並不提供分割線屬性。這樣,只能開動勞動人民的智慧來嘗試解決了。(今天進行了三次嘗試,成功的方式在第三次嘗試中。 歡迎大家提供更好的實現方法)第一次嘗試:item的背景圖
這個方法實際上是從網上看到的,使用最多的解決方案。 簡而言之,就是對GridView的每個單元做背景圖。程式碼說話:
在item_gridview中,已矩形框為背景:<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_margin="0.0dip" > <RelativeLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_centerInParent="true" android:background="@drawable/item_gridview" android:padding="12.0dip" > <ImageView android:id="@+id/gridview_item_icon" android:layout_width="58.0dip" android:layout_height="58.0dip" android:layout_centerHorizontal="true" android:contentDescription="@string/app_name" /> <TextView android:id="@+id/gridview_item_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/gridview_item_icon" android:layout_centerHorizontal="true" android:layout_marginTop="5.0dip" android:maxLines="1" android:textSize="14.0sp" /> </RelativeLayout> </RelativeLayout>
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true"><shape android:shape="rectangle"> <stroke android:width="1px" android:color="@color/pink" /> <gradient android:angle="270.0" android:endColor="#ffe8ecef" android:startColor="#ffe8ecef" /> </shape></item> <item android:state_focused="true"><shape android:shape="rectangle"> <gradient android:angle="270.0" android:endColor="#ffe8ecef" android:startColor="#ffe8ecef" /> <stroke android:width="1px" android:color="@color/pink" /> </shape></item> <item><shape android:shape="rectangle"> <gradient android:angle="270.0" android:endColor="#ffffffff" android:startColor="#ffffffff" /> <stroke android:width="1px" android:color="@color/pink" /> </shape></item> </selector>
在Activity中對GridView設定Adapter,充入物件,效果如下圖所示。(目前僅探究gridview,其他部分還比較醜)
雖然在xml中設定的是最小值 1px , 然而看起來還是比支付寶錢包的效果粗很多。 因為後者才是 真·1px ,前者的分割線實際上是相鄰單元的邊框合併在了一起,所以看起來要粗很多。
第二次嘗試:相鄰單元只有一個背景
上面的問題已經可以看出,是相鄰的單元格背景框相鄰使分割線變粗。 那麼可以提出相應的解決辦法:相鄰單元格只有一個有背景。
以當前4列的情況為例,實際上,可以去掉:
第一行的第二,第四個單元
第二行的第一,第三個單元
第三行的第二,第四個單元
。。。
可以總結出規律,通過去掉行列數奇偶性不同的單元,可以使相鄰的單元(包括上下相鄰和左右相鄰)不同時具有邊框。在adapter的getView方法中加入如下程式碼:
if(position/4%2!=position%2){
RelativeLayout layout=(RelativeLayout)convertView.findViewById(R.id.layout_gridview);
layout.setBackgroundResource(R.drawable.item_gridview2);
}
其中item_gridview2是邊框為空白的背景
效果圖:
這是才發現,我犯了一個很2的錯誤:小看了1px的誤差
當相鄰單元的邊框去除後,成對角方向的單元框對不齊了。。
第三次嘗試:暴力加框
這一次比較直接和暴力,最終達到了預想的效果。但是顯得不是那麼聰明和巧妙,這裡拋磚引玉,希望路過的大牛能給出更好的實現方法。
這一次,通過在單元格xml中插入兩個寬度為1px的ImageView做邊框,來實現gridview的偽分割線:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_weight="1"
android:gravity="center"
android:id="@+id/layout_gridview_item"
android:orientation="vertical"
>
<ImageView
android:layout_marginTop="12dp"
android:id="@+id/gridview_item_icon"
android:layout_width="38.0dip"
android:layout_height="38.0dip"
android:layout_centerHorizontal="true"
android:background="@drawable/guan"
android:contentDescription="@string/app_name" />
<TextView
android:id="@+id/gridview_item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/gridview_item_icon"
android:layout_centerHorizontal="true"
android:maxLines="1"
android:text="標題"
android:textColor="@color/darkblue"
android:layout_marginBottom="12dp"
android:textSize="14.0sp" />
<ImageView
android:layout_width="match_parent"
android:id="@+id/line_bottom"
android:layout_height="1dp"
android:background="@color/lightgray"
android:layout_gravity="bottom"/>
</LinearLayout>
<ImageView
android:layout_width="1dp"
android:layout_weight="0"
android:layout_height="match_parent"
android:background="@color/lightgray"
android:layout_gravity="bottom"
/>
</LinearLayout>
</LinearLayout>
最終效果。。第四次嘗試:重寫dispatchDraw 方法
這裡我們可以對gridview進行重繪, 重寫父類的dispatchDraw方法。
思路基本跟方法三一致,對每個子item畫兩條框。不同之處在於,當我們採用動態繪製(而不是寫死在xml)時,可以針對不同的情況進行判斷和定製,使最後的效果更完美。 在這裡,可以考慮到左右上下的對稱性和gridview的item數不是列數的整數倍時的情況。 重寫程式碼如下:
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
View localView1 = getChildAt(0);
int column = getWidth() / localView1.getWidth();
int childCount = getChildCount();
Paint localPaint= new Paint();
localPaint.setStyle(Paint.Style.STROKE);
localPaint.setColor(getContext().getResources().getColor(R.color.lightgray));
for(int i = 0;i < childCount;i++){
View cellView = getChildAt(i);
if((i + 1) % column == 0){ //最右側只畫底部邊框
canvas.drawLine(cellView.getLeft(), cellView.getBottom(), cellView.getRight(), cellView.getBottom(), localPaint);
}else if((i + 1) > (childCount - (childCount % column))){//最下部只畫右側邊框
canvas.drawLine(cellView.getRight(), cellView.getTop(), cellView.getRight(), cellView.getBottom(), localPaint);
}else{//畫底部和右側邊框
canvas.drawLine(cellView.getRight(), cellView.getTop(), cellView.getRight(), cellView.getBottom(), localPaint);
canvas.drawLine(cellView.getLeft(), cellView.getBottom(), cellView.getRight(), cellView.getBottom(), localPaint);
}
}
//如果最後一行不足,則補全預設item的右側豎線。
if(childCount % column != 0){
for(int j = 0 ;j < (column-childCount % column) ; j++){
View lastView = getChildAt(childCount - 1);
canvas.drawLine(lastView.getRight() + lastView.getWidth() * j, lastView.getTop(), lastView.getRight() + lastView.getWidth()* j, lastView.getBottom(), localPaint);
}
}
}
效果與第三種方法相同。