Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 談談Android中的Divider是個什麼東東

談談Android中的Divider是個什麼東東

編輯:關於Android編程

在Android應用開發中會經常碰到一個叫divider的東西,就是兩個View之間的分割線。最近工作中注意到這個divider並分析了一下,竟然發現內有乾坤,驚為天人…

ListView的divider

1. 定制divider的邊距

ListView的divider默認是左右兩頭到底的,如何簡單的設置一個邊距呢?

利用inset或者layer-list都可以簡單的實現,代碼如下:

<!-- 方法一 -->
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetLeft="16dp" >
<shape android:shape="rectangle" >
<solid android:color="#f00" />
</shape>
</inset>
<!-- 方法二 -->
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:left="16dp">
<shape android:shape="rectangle">
<solid android:color="#f00" />
</shape>
</item>
</layer-list>

其中inset除了左邊距insetLeft, 還有insetTop、insetRight、insetBottom, 效果圖:

2. 最後一項的divider

很多同學可能發現了,ListView最後一項的divider有時候有,有時候又沒有。

我畫個圖大家就都能理解了:

 

上面是數據不足的顯示效果,如果數據滿屏的話,都是看不多最後的divider的。

真相是,當ListView高度是不算最後一項divider的,所以只有在match_parent的情況下,ListView的高度是有余的,才能畫出最後的那個divider。

ps:網上很多資料,把最後一項的divider和footerDividersEnabled混在一起了,這個是不對的,兩個從邏輯上是獨立的,類似的還有一個headerDividersEnabled,headerDividersEnabled和footerDividersEnabled不會影響到默認情況下最後的divider的繪制,他們是給header和footer專用的,特此說明。

RecyclerView的Divider

RecyclerView的Divider叫做ItemDecoration,RecyclerView.ItemDecoration本身是一個抽象類,官方沒有提供默認實現。

官方的Support7Demos例子中有個DividerItemDecoration, 我們可以直接參考一下,位置在sdk的這裡:

extras/android/support/samples/Support7Demos/src/…/…/decorator/DividerItemDecoration.java

但是這個DividerItemDecoration有三個問題:

只支持系統默認樣式,不支持自定義Drawable類型的divider

裡面的算法對於無高寬的Drawable(比如上面用到的InsetDrawable)是畫不出東西的水平列表的Divider繪制方法drawHorizontal()的right計算有誤,導致垂直Divider會繪制不出來,應該改為:final int right = left + mDivider.getIntrinsicWidth();;

針對這幾個問題,我修復並增強了一下:

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
/**
* RecyclerView的ItemDecoration的默認實現
* 1. 默認使用系統的分割線
* 2. 支持自定義Drawable類型
* 3. 支持水平和垂直方向
* 4. 修復了官方垂直Divider顯示的bug
* 擴展自官方android sdk下的Support7Demos下的DividerItemDecoration
*/
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private Drawable mDivider;
private int mWidth;
private int mHeight;
private int mOrientation;
public DividerItemDecoration(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
setOrientation(orientation);
}
/**
* 新增:支持自定義dividerDrawable
*
* @param context
* @param orientation
* @param dividerDrawable
*/
public DividerItemDecoration(Context context, int orientation, Drawable dividerDrawable) {
mDivider = dividerDrawable;
setOrientation(orientation);
}
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
/**
* 新增:支持手動為無高寬的drawable制定寬度
* @param width
*/
public void setWidth(int width) {
this.mWidth = width;
}
/**
* 新增:支持手動為無高寬的drawable制定高度
* @param height
*/
public void setHeight(int height) {
this.mHeight = height;
}
@Override
public void onDraw(Canvas c, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
public void drawVertical(Canvas c, RecyclerView parent) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin +
Math.round(ViewCompat.getTranslationY(child));
final int bottom = top + getDividerHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getRight() + params.rightMargin +
Math.round(ViewCompat.getTranslationX(child));
final int right = left + getDividerWidth();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
outRect.set(0, 0, 0, getDividerHeight());
} else {
outRect.set(0, 0, getDividerWidth(), 0);
}
}
private int getDividerWidth() {
return mWidth > 0 ? mWidth : mDivider.getIntrinsicWidth();
}
private int getDividerHeight() {
return mHeight > 0 ? mHeight : mDivider.getIntrinsicHeight();
}
}

使用如下:

// 默認系統的divider
dividerItemDecoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST);
// 自定義圖片drawable分的divider
dividerItemDecoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST, getResources().getDrawable(R.drawable.ic_launcher));
// 自定義無高寬的drawable的divider - 垂直列表
dividerItemDecoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST, new ColorDrawable(Color.parseColor("#ff00ff")));
dividerItemDecoration.setHeight(1);
// 自定義無高寬的drawable的divider - 水平列表
dividerItemDecoration = new DividerItemDecoration(this, DividerItemDecoration.HORIZONTAL_LIST, new ColorDrawable(Color.parseColor("#ff00ff")));
dividerItemDecoration.setWidth(1);
// 自定義帶邊距且無高寬的drawable的divider(以上面InsetDrawable為例子)
// 這個地方也可以在drawable的xml文件設置size指定寬高,效果一樣
dividerItemDecoration = new DividerItemDecoration(this, DividerItemDecoration.HORIZONTAL_LIST, getResources().getDrawable(R.drawable.list_divider));
dividerItemDecoration.setWidth(DisplayLess.$dp2px(16) + 1);

手動的Divider

有的時候沒有系統控件的原生支持,只能手動在兩個view加一個divider,比如,設置界面每項之間的divider,水平平均分隔的幾個view之間加一個豎的divider等等。

無論橫的豎的,都非常簡單,定一個View,設置一個background就可以了,正常情況下沒什麼好說的。

下面我們來考慮一種常見設置界面,這種設置界面的分割線是有左邊距的,比如微信的設置界面,我相信絕大部分人的布局代碼都是這樣實現的:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--這個group_container的background一定要設置,
而且要和list_item_bg的list_item_normal一致,
否則效果會不正確。 -->
<LinearLayout
android:id="@+id/group_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginTop="48dp"
android:background="#fff"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/account_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/list_item_bg"
android:clickable="true">
<TextView
android:id="@+id/account_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_margin="16dp"
android:text="First Item"
android:textColor="#f00"
android:textSize="16sp" />
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginLeft="16dp"
android:background="#f00" />
<RelativeLayout
android:id="@+id/phone_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/list_item_bg"
android:clickable="true">
<TextView
android:id="@+id/phone_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_margin="16dp"
android:text="Second Item"
android:textColor="#f00"
android:textSize="16sp" />
</RelativeLayout>
</LinearLayout>
</RelativeLayout>

效果圖如下,順便我們也看看它的Overdraw狀態:

 

通過分析Overdraw的層次,我們發現為了一個小小的邊距,設置了整個groud_container的背景,從而導致了一次Overdraw。

能不能優化掉這個Overdraw?答案是肯定的。

背景肯定要去掉,但是這個左邊距的View就不能這麼簡單的寫了,需要自定義一個View,它要支持能把左邊距的空出的16dp的線用list_item_normal的顏色值繪制一遍,這樣才能看的出左邊距。

這個View具體代碼如下:

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import com.jayfeng.lesscode.core.R;
public class SpaceDividerView extends View {
private int mSpaceLeft = 0;
private int mSpaceTop = 0;
private int mSpaceRight = 0;
private int mSpaceBottom = 0;
private int mSpaceColor = Color.TRANSPARENT;
private Paint mPaint = new Paint();
public SpaceDividerView(Context context) {
this(context, null);
}
public SpaceDividerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SpaceDividerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SpaceDividerView, defStyleAttr, 0);
mSpaceLeft = a.getDimensionPixelSize(R.styleable.SpaceDividerView_spaceLeft,
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0, getResources().getDisplayMetrics()));
mSpaceTop = a.getDimensionPixelSize(R.styleable.SpaceDividerView_spaceTop,
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0, getResources().getDisplayMetrics()));
mSpaceRight = a.getDimensionPixelSize(R.styleable.SpaceDividerView_spaceRight,
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0, getResources().getDisplayMetrics()));
mSpaceBottom = a.getDimensionPixelSize(R.styleable.SpaceDividerView_spaceBottom,
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0, getResources().getDisplayMetrics()));
mSpaceColor = a.getColor(R.styleable.SpaceDividerView_spaceColor, Color.TRANSPARENT);
a.recycle();
mPaint.setColor(mSpaceColor);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mSpaceLeft > 0) {
canvas.drawRect(0, 0, mSpaceLeft, getMeasuredHeight(), mPaint);
}
if (mSpaceTop > 0) {
canvas.drawRect(0, 0, getMeasuredWidth(), mSpaceTop, mPaint);
}
if (mSpaceRight > 0) {
canvas.drawRect(getMeasuredWidth() - mSpaceRight, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
}
if (mSpaceBottom > 0) {
canvas.drawRect(0, getMeasuredHeight() - mSpaceBottom, getMeasuredWidth(), getMeasuredHeight(), mPaint);
}
}
}

用這個SpaceDividerView我們重寫一下上面的布局代碼:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:id="@+id/group_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginTop="48dp"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/account_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/list_item_bg"
android:clickable="true">
<TextView
android:id="@+id/account_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_margin="16dp"
android:text="First Item"
android:textColor="#f00"
android:textSize="16sp" />
</RelativeLayout>
<com.jayfeng.lesscode.core.other.SpaceDividerView
android:layout_width="match_parent"
android:layout_height="1px"
android:background="#f00"
app:spaceLeft="16dp"
app:spaceColor="@color/list_item_normal"/>
<RelativeLayout
android:id="@+id/phone_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/list_item_bg"
android:clickable="true">
<TextView
android:id="@+id/phone_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_margin="16dp"
android:text="Second Item"
android:textColor="#f00"
android:textSize="16sp" />
</RelativeLayout>
</LinearLayout>
</RelativeLayout>

效果圖和Overdraw狀態如下:

 

界面中group_container那塊由之前的綠色變成了藍色,說明減少了一次Overdraw。

上述情況下,SpaceDividerView解耦了背景色,優化了Overdraw,而且這個SpaceDividerView也是支持4個方向的,使用起來特別方便。

陰影divider

陰影分割線的特點是重疊在下面的view之上的,它的目的是一種分割線的立體效果。

 

使用RelativeLayout並控制上邊距離可以實現:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- layout_marginTop的值應該就是不包括陰影高度的header高度-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_marginTop="@dimen/header_height"
android:orientation="vertical">
</LinearLayout>
<!-- 這個要放在最後,才能顯示在最上層,這個header裡面包括一個陰影View-->
<include
android:id="@+id/header"
layout="@layout/include_header" />
</RelativeLayout>

雖然再簡單不過了,還是稍微分析一下,header包括內容48dp和陰影8dp,那麼marginTop就是48dp了。

下面給大家介紹Android給ListView設置分割線Divider樣式

給ListView設置分割線,只需設置如下兩個屬性:

android:divider="#000" //設置分割線顯示顏色
android:dividerHeight="1px" //此處非0,否則無效

<ListView android:id="@+id/listView"   
 android:layout_width="fill_parent"  
 android:layout_height="fill_parent"   
 android:divider="#FFF"  
 android:dividerHeight="1px"  
 android:layout_margin="10dip"/>

以上內容給大家簡單介紹了Android中的Divider,希望對大家有所幫助!

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved