### 猴年猴賽雷啊各位,今天沒吃藥我感覺自己萌萌噠!
qq和微信和支付寶紅包大戰,不知道各位的戰績是多少嘞? 反正我qq搶到的都是氣泡。因為太不爽,所以自己寫一個下拉搶紅包自己玩(自己跟自己玩)。
先來看效果圖。這個…… 呃~~ -**__**-” 。。有點丑 是低仿。?

轉載請注明出處:[http://blog.csdn.net/wingichoy/article/details/50662592](http://blog.csdn.net/wingichoy/article/details/50662592)
### 學習完本篇博客你能獲得到的知識
1. 正確的獲得view的大小
2. listview的下拉header
3. 自定義字體
4. 自己添加監聽器
## 廢話不多說,快跟我來一起動手
首先來跟我打造一個自定義的listview
新建一個類,繼承自listview,這里需要來重寫一下他的overScrollBy()方法。
~~~
public class HBListView extends ListView {
//header顯示的圖片
private MyImageView mImageView;
private Context mContext;
//搶到紅包時候的監聽器
private OnSuccessListener mOnSuccessListener;
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
Log.e("wing", "deltaY:" + deltaY + " scrollY:" + scrollY);
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
}
~~~
先來介紹一下重要參數的意思
> deltaX,Y是指速度值,也就是你手指滑動的瞬時速度。?
> scrollX,Y 水平和數值方向上的變化量?
> isTouchEvent 表示手指是否在觸摸狀態
觀察log可以輕易看到他們與手機觸摸的關系。大家自己試驗一下就可以。
接著給listview加一個header
~~~
mListView = (HBListView) findViewById(R.id.lv);
final View header = LayoutInflater.from(this).inflate(R.layout.view_header,null);
mListView.addHeaderView(header);
~~~
其實這個header就是個圖片。 我們剛才從listview的監聽上面看到了偏移量。所以可以根據偏移量來動態改變圖片的大小。
~~~
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.wingsofts.hongbao.MyImageView
android:id = "@+id/imageView"
android:scaleType="centerCrop"
android:layout_width="match_parent"
android:layout_height="0dp"
android:src="@drawable/bg" />
</LinearLayout>
~~~
因為header剛開始是隱藏的,所以這里高度設置為0.
這里來一個小插曲。。不知道大家有沒有在activity中獲取view的高度呢。是不是有時候只能拿到0。對于這個問題呢。正確的獲取view寬高有如下幾種解決方案。
> 1.onWindowsFocusChanged()?
> 2.view.postRunnable() 將一個runnable投遞到消息隊列尾部。?
> 3.ViewTreeObserver
這里采用第三種方法,在OnGlobalLayoutListener中 調用listview的changSize方法。。
~~~
header.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mListView.changeSize(image);
header.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
~~~
這里看看listview的changsize方法,其實只是將header的引用傳來而已..這是因為之前在這里獲取高度,后來又去掉了。。偷懶沒有改方法名
~~~
public void changeSize(MyImageView imageView) {
mImageView = imageView;
}
~~~
拿到了圖片之后,理所當然是根據手指來改變圖片的大小啦。 還記得onScrollBy里面的幾個參數嗎,我們只需要在 (觸摸的時候&&下滑的時候) 改變圖片的大小就可以了。?
下滑的狀態可以根據log得知為deltaY<0.
~~~
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
Log.e("wing", "deltaY:" + deltaY + " scrollY:" + scrollY);
//給圖片一個最大值
if (mImageView.getHeight() < 300) {
//是觸摸狀態 以及 是下滑狀態
if (isTouchEvent && deltaY < 0) {
//動態改變imageView的大小
mImageView.getLayoutParams().height += Math.abs(deltaY);
mImageView.requestLayout();
}
}
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
}
~~~
這樣便可以下拉了,現在的效果是這樣的。?

咦。。是可以下拉了。但是沒有回彈,怎么辦!
還能怎么辦 寫一個就是了。思路就是用一個動畫,來不停地改變header高度。
### ObjectAnimator的使用
他的使用方法很簡單,他通過反射調用set方法來改變view的屬性然后發生動畫。這里直接上例子,首先重寫一下imageview,添加一個setHeight()方法.
~~~
public class MyImageView extends ImageView {
private Paint mPaint;
public void setHeight(int height) {
getLayoutParams().height = height;
requestLayout();
}
}
~~~
然后在使用ObjectAnimator的ofInt方法,獲取到一個ObjectAnimator,這里ofXXX具體看參數類型。
~~~
//獲取mImageView的setHeight方法,數值從當前圖片的高度逐漸到0,產生動畫
private void closeHeader() {
ObjectAnimator oa = ObjectAnimator.ofInt(mImageView, "height", mImageView.getHeight(), 0);
oa.start();
}
~~~
這時候關閉動畫就做完啦。想要關閉的時候 只需要在action_up調用closeHeader()方法即可.現在是這樣的:?

~~~
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
closeHeader();
break;
}
~~~
咦,有沒有似曾相識的趕腳,沒錯!!這特么的不就是下拉刷新么。 好了本篇博客到此結束!!

額。。你先把手里的刀放下w(?Д?)w!!!?
剛才結束的是下拉刷新的博客,現在繼續寫搶紅包的博客。。。
思路就是用一個變量來保存連刷的次數,再用隨機數判斷是否抽中紅包。?
給MyImageView一個public的屬性次數
~~~
public class MyImageView extends ImageView {
public int mTime;
}
~~~
然后只要在ACTION_UP的時候,判斷隨機數有沒有抽中,決定次數是否累加:
~~~
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
int ran = (int) (Math.random() * 10);
//設置中獎概率
if (ran > 1) {
//如果沒中次數累加
mImageView.mTime++;
closeHeader();
} else {
//否則為中獎
mImageView.mTime = 0;
if (mOnSuccessListener != null) {
mOnSuccessListener.onSuccess();
}
closeHeader();
}
break;
}
return super.onTouchEvent(ev);
}
~~~
邏輯是不是粉簡單! 聰明的你已經看到了搶紅包成功的回調。這個等等說,先說拿到了次數以后怎么做。
拿到了次數之后理所應道就是把文字畫上去啦~~ 這里為了方便就給了一個固定的位置。因為數字大小跟文字不一樣,所以把字符串分了三串來畫..
~~~
public int mTime;
private Typeface mTypeFace;
private float mTxtHLength;
private float mTxtRLength;
private String txtH = "連刷";
private String txtR = "次,加油!";
public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mPaint.setColor(Color.YELLOW);
mMidPaint = new Paint();
mMidPaint.setColor(Color.YELLOW);
mTypeFace = Typeface.createFromAsset(context.getAssets(), "ifont.ttf");
mMidPaint.setTypeface(mTypeFace);
mPaint.setTypeface(mTypeFace);
//設置文字大小
mPaint.setTextSize(50);
//設置數字大小
mMidPaint.setTextSize(100);
//測量頭文字的長度
mTxtHLength = mPaint.measureText(txtH);
//測量尾文字的長度
mTxtRLength = mPaint.measureText(txtR);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String count = mTime + "";
//數字的長度
float countLength = mMidPaint.measureText(count);
//總體長度
float totalLength = mTxtRLength + countLength;
//畫在正中間
float start = getMeasuredWidth() / 2 - totalLength / 2;
canvas.drawText(txtH, 100, getMeasuredHeight() / 2, mPaint);
canvas.drawText(count, start + countLength / 2, getMeasuredHeight() / 2, mMidPaint);
canvas.drawText(txtR, start + countLength / 2 + mTxtRLength / 2, getMeasuredHeight() / 2, mPaint);
}
~~~
這樣就把次數提示畫上去了!來看看效果:?
?
這個…… 呃~~ -**__**-” 是畫上去了,但是
### 字!體!好!丑!
沒事沒事,別著急。。我們換一個字體就是了。
### Paint字體的改變
> 1.首先下載一個字體 放到assets文件夾下?
> 2.獲取到字體的引用?
> 3.給畫筆設置字體
~~~
//改變字體,就是這么輕松自如~~
mTypeFace = Typeface.createFromAsset(context.getAssets(), "ifont.ttf");
mMidPaint.setTypeface(mTypeFace);
mPaint.setTypeface(mTypeFace);
~~~
看效果圖:?

吼吼吼~~~ 有點感覺了。
### 監聽器的添加
先來回過頭來,大家知道View有setOnClickListener.. 那么我們也來加一個紅包成功監聽器吧。?
在ListView里面增加一個內部接口,添加一個set方法
~~~
private OnSuccessListener mOnSuccessListener;
interface OnSuccessListener {
void onSuccess();
}
public void setOnSuccessListener(OnSuccessListener onSuccessListener) {
mOnSuccessListener = onSuccessListener;
}
~~~
在ACTION_UP成果邏輯里面調用onSuccess()… 這就是監聽器。。簡單吧~就是個回調
現在在MainActivity里使用這個搶紅包listView吧~?
設置一個監聽器,里面寫你想要的事件,中幾千萬的隨便寫,總比氣泡好,,
~~~
mListView.setAdapter(new ArrayAdapter(this,android.R.layout.simple_list_item_1,datas));
mListView.setOnSuccessListener(new HBListView.OnSuccessListener() {
@Override
public void onSuccess() {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setMessage("恭喜中獎!抽到了疼遜聊天氣泡!").setNegativeButton("確認", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
}).show();
}
});
~~~
這樣就能出現文章頂部預覽圖的效果了:

如果你喜歡我的博客,請繼續關注我,求贊求頂~求吐槽
本項目地址:[打開鏈接](https://github.com/githubwing/QQHongBao)
———————————–華麗麗的分割線————————————
### 效果優化
處于追求完美的心態,群友 小情歌 對代碼做了一些優化,主要有
> 1.修復上滑也顯示紅包的bug?
> 2.優化動畫事件,在松手的過程中如果繼續ACTION_DOWN則放棄動畫?
> 3.增加視差特效
修改其實也不復雜 具體看這一段代碼:
~~~
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
x1 = ev.getX();
y1 = ev.getY();
if (oa != null) {
if (oa.isRunning()) {
oa.cancel();
}
}
if (mImageView.getHeight() == 0) {
mImageView.mTime = 0;
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
x2 = ev.getX();
y2 = ev.getY();
if (y1 - y2 > 0) {
b = false;
} else if (y2 - y1 > 0) {
b = true;
}
int ran = (int) (Math.random() * 100);
Log.e("wing", ran + "");
if (b) { //往下滑
if (ran > 3) {
mImageView.mTime++;
closeHeader();
} else {
mImageView.mTime = 0;
if (mOnSuccessListener != null) {
mOnSuccessListener.onSuccess();
}
closeHeader();
}
}
break;
}
return super.onTouchEvent(ev);
}
~~~
相信大家都看得懂,這里就不一一說明了
- 前言
- android自定義viewgroup初步之一----抽屜菜單
- Android 自定義view --圓形百分比(進度條)
- Android 自定義View -- 簡約的折線圖
- 新手自定義view練習實例之(一) 泡泡彈窗
- 新手自定義view練習實例之(二) 波浪view
- 手把手帶你畫一個 時尚儀表盤 Android 自定義View
- 手把手帶你畫一個動態錯誤提示 Android自定義view
- 手把手帶你做一個超炫酷loading成功動畫view Android自定義view
- 關于Android自定義view 你所需要知道的基本函數
- Android自定義view進階-- 神奇的貝塞爾曲線
- wing帶你玩轉自定義view系列(1) 仿360內存清理效果
- wing帶你玩轉自定義view系列(2) 簡單模仿qq未讀消息去除效果
- wing帶你玩轉自定義view系列(3)模仿微信下拉眼睛
- 手把手教你畫一個 逼格滿滿圓形水波紋loadingview Android
- 有坑?? 為何wing墜入PorterDuffXferMode的萬丈深淵(PorterDuffXferMode深入試驗)
- 手把手帶你畫一個漂亮蜂窩view Android自定義view
- 一個炫字都不夠??!!!手把手帶你打造3D自定義view
- 恭喜發財! -- 手把手教你仿造一個qq下拉搶紅包 Android自定義view