今天無聊自己寫了一個模仿viewpager的指示器,這個例子是利用我以前寫的一片文章的例子進行改進《android橫向滾動屏幕特效分析》。
下面說下我的思路,首先當控件初始化的時候獲取ScrollLayout空間的子View控件個數,然後傳入我們要寫的IndictorView類裏面,然後利用onDraw()方法在屏幕上畫同等數量的圓形,表示當前屏幕的圓圈我們用不同的顏色區分就行了,然後每次移動的時候我們改變畫表示當前屏幕圓圈的位置即可。
既然要畫兩種顏色的圓圈那麼就要生成兩種顏色的畫筆,下面是我生成畫筆的代碼:
/**
* 生成標識當前屏幕的畫筆和背景圓圈的畫筆,此方法在構造方法中調用
*/
private void init() {
// 這句是讓畫筆畫得更細膩(抗鋸齒)
fontPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
// 這句話的意思是已填充的方式畫園,Style.STROKE方式是畫一個圓環中間沒有填充
fontPaint.setStyle(Style.FILL);
// 設置畫筆的顏色
fontPaint.setColor(Color.RED);
backPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
backPaint.setStyle(Style.FILL);
backPaint.setColor(Color.BLACK);
}
在構造好IndictorView之後我們需要把ScorllView的子元素個數傳過來,那麼就需要提供一個方法讓外部可以把我們需要的數據傳如,下面是實現代碼:
/**
* 初始化圓的個數,這裏的屏幕寬度是爲了後面我們計算圓圈的位置時使用
*
* @param circleNumber
* 園的數量
* @param motionWidth
* 屏幕的寬度
*/
public void init(int circleNumber, float motionWidth) {
this.circleNumber = circleNumber;
this.motionWidth = motionWidth;
}
現在我們有了園的個數,又得到屏幕的寬度,那麼就可以開始畫園了,下面是實現代碼:
@Override
public void draw(Canvas canvas) {
// 得到圓的Y軸座標
int height = getHeight() - getPaddingTop() - (radius * 2);
// 這裏是根據需要畫圓的個數使用一個循環遍歷來畫園
for (int i = 1; i <= circleNumber; i++) {
// 噹噹前的圓的下標等於要畫的園的下標時候,說明這個下標就是標識當前屏幕下標的園
if (curentCircleNumber == i) {
// 這裏的表達式主要是計算園的X軸座標
canvas.drawCircle((motionWidth - (radius * 2
* (circleNumber - i) + (circlePadding * (circleNumber
- i - 1)))) / 2.0f + 10, height, radius, fontPaint);
} else {
// 畫背景圓圈
canvas.drawCircle((motionWidth - (radius * 2
* (circleNumber - i) + (circlePadding * (circleNumber
- i - 1)))) / 2.0f + 10, height, radius, backPaint);
}
}
super.draw(canvas);
// 刷新屏幕
invalidate();
}
上面就是核心代碼,那麼有人會問當OnDraw()執行過後怎樣改變圓圈的位置能,其實View的onDraw()方法是在這個view被渲染後一直在後臺執行的,所以不同擔心我們改變curentCircleNumber的值時會不會從繪圓的位置的問題。
最後大家別忘了一件事,我們在重寫空間的時候一定要記得覆蓋onMeasure()方法,這裏就不多說了,下面是源碼:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureWidth(widthMeasureSpec),
measureHeight(heightMeasureSpec));
}
private int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
// 當控件設置全屏時
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
float temp = circleNumber - 2 * radius;
result = (int) (getPaddingLeft() + getPaddingRight()
+ (circleNumber * 2 * radius) + (circleNumber - 1) * temp + 1);
// 當設置根據內容自動適應
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}
private int measureHeight(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
// 當空間設置全屏時
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
result = (int) (4 * radius + getPaddingTop() + getPaddingBottom() + 1);
// 當設置根據內容自動適應
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}
這時我們的IndictorView類我們就完全寫好了,下面是我的佈局文件menu.xml:
<?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" >
<cn.com.karl.scrollview.ScrollLayout
android:id="@+id/scorllView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/white" >
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="fitCenter"
android:src="@drawable/test1" />
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="fitCenter"
android:src="@drawable/test2" />
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="fitCenter"
android:src="@drawable/test3" />
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="fitCenter"
android:src="@drawable/test1" />
</cn.com.karl.scrollview.ScrollLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/scorllView"
android:background="#88252525" >
<cn.com.karl.scrollview.IndictorView
android:id="@+id/indictorView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</LinearLayout>
</RelativeLayout>
這裏值得注意的是android:background="#88252525"這個背景色是爲了給我們的IndictorView控件加上透明背景色,這個可以選擇性添加。
下面是主程序使用源碼:
package cn.com.karl.activity;
import android.app.Activity;
import android.os.Bundle;
import android.view.Display;
import cn.com.karl.R;
import cn.com.karl.scrollview.IndictorView;
import cn.com.karl.scrollview.ScrollLayout;
public class TestScrollLayout extends Activity {
private ScrollLayout scrollLalyout;
private IndictorView indictorView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.menu);
initView();
}
private void initView() {
scrollLalyout = (ScrollLayout) findViewById(R.id.scorllView);
indictorView = (IndictorView) findViewById(R.id.indictorView);
Display display = getWindowManager().getDefaultDisplay();
indictorView.init(scrollLalyout.getChildCount(), display.getWidth());
scrollLalyout.setIndictorView(indictorView);
}
}
scrollLalyout.setIndictorView(indictorView);這一句是把我們的指示器放到ScrollLayout裏面去,當屏幕移動的時候可以調用我們的IndictorView的setCurentCircleNumber()方法來時時改變下標.
下面是ScrollLayout裏面的移動屏幕的方法源碼:
/**
* 移動到下一個屏幕
*
* @param whichScreen
* 下一個屏幕的下標識
*/
public void snapToScreen(int whichScreen) {
// get the valid layout page
whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
if (getScrollX() != (whichScreen * getWidth())) {
final int delta = whichScreen * getWidth() - getScrollX();
mScroller.startScroll(getScrollX(), 0, delta, 0, 1000);
mCurScreen = whichScreen;
invalidate(); // Redraw the layout
indictorView.setCurentCircleNumber(whichScreen + 1);
}
}
好了這裏就寫完了,有不足的地方請大家指正!下面是效果圖