編輯:關於Android編程
用過多米音樂的都市知道, 這個UI可以上下滑動,作用嘛---無聊中可以劃劃解解悶,這被錘子公司老羅稱謂為“情懷”,其實叫“情味”更合適。嘿嘿.如今挪動互聯網開展這麼迅速,市場上已不再是那早期隨便敲個APP放上架就能具有幾十萬用戶的階段了.近來蘋果公司,為了怕android下載量趕超蘋果商店,大勢宣稱:(第 500 億個下載應用的用戶就能夠獲得 10,000 美元的 iTunes 禮品卡,除此之外,緊隨第 500 億以後的前 50 名用戶也可以獲得 500 美元的禮品卡.至於挪動開展趨勢,我想搞挪動IT的人心裡都比擬清楚,扯遠了).其實應用UI殊效是應用中很大的一部分,如果同樣功能的兩款軟件,一個功能好點如“網易新聞”,另外一個略微差點如“新浪新聞”,用戶的你毫無疑難確定會選擇網易客戶端.總結就是“操作性”對於產品起著至關重要的因素.
接下來我們看下如何實現,首先聲明,這個實現的方式不是很好,我這裡只是提出一個解決方案,大家可以根據自己的想法進行創新.
道理:RelativeLayout+自定義ScrollView.
我們大致看下布局結構如圖:
其實也沒什麼技術含量,我簡單介紹下:紅色代表的是背景照片,綠色的代表自定義ScrollView,粉色是代表你要編輯的透明區域.也不過多解釋,想必大家都明確,我們還是來看代碼吧。
由於屬於情懷殊效(沒有具體的回調事件要求),那麼就沒有必要自定義監聽,回調處理,我直接把要處理的UI注入到自定義控件中,這樣她方便我也方便.
在此說明一下,前面部分實現中有誤,但是也希望您仔細品讀,相信您必定可以學到一些知識的。
首先我們將背景圖片和頂部線條注入到該控件中。接著我們看onTouchEvent事件,因為至始至終都是她在起作用.
復制代碼 代碼如下:
/***
* 觸摸事件
*
* @param ev
*/
public void commOnTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
initTouchY = ev.getY();
current_Top = initTop = imageView.getTop();
current_Bottom = initBottom = imageView.getBottom();
lineUp_current_Top = line_up_top = line_up.getTop();
lineUp_current_Bottom = line_up_bottom = line_up.getBottom();
break;
case MotionEvent.ACTION_UP:
/** 回縮動畫 **/
if (isNeedAnimation()) {
animation();
}
isMoveing = false;
touchY = 0;// 手指松開要歸0.
break;
/***
* 消除出第一次挪動計算,因為第一次無法得知deltaY的高度, 然而我們也要進行初始化,就是第一次挪動的時候讓滑動距離歸0.
* 以後記載精確了就正常執行.
*/
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "isMoveing=" + isMoveing);
touchY = ev.getY();
float deltaY = touchY - initTouchY;// 滑動距離
Log.e(TAG, "deltaY=" + deltaY);
/** 過濾: **/
if (deltaY < 0 && inner.getTop() <= 0) {
return;
}
// 當滾動到最上或者最下時就不會再滾動,這時挪動布局
isNeedMove();
if (isMoveing) {
// 初始化頭部矩形
if (normal.isEmpty()) {
// 保存正常的布局位置
normal.set(inner.getLeft(), inner.getTop(),
inner.getRight(), inner.getBottom());
}
// 挪動布局(手勢挪動的1/3)
float inner_move_H = deltaY / 5;
inner.layout(normal.left, (int) (normal.top + inner_move_H),
normal.right, (int) (normal.bottom + inner_move_H));
/** image_bg **/
float image_move_H = deltaY / 10;
current_Top = (int) (initTop + image_move_H);
current_Bottom = (int) (initBottom + image_move_H);
imageView.layout(imageView.getLeft(), current_Top,
imageView.getRight(), current_Bottom);
/** line_up **/
float line_up_H = inner_move_H;
lineUp_current_Top = (int) (line_up_top + inner_move_H);
lineUp_current_Bottom = (int) (line_up_bottom + inner_move_H);
line_up.layout(line_up.getLeft(), lineUp_current_Top,
line_up.getRight(), lineUp_current_Bottom);
}
break;
default:
break;
}
}
簡單說明:
MotionEvent.ACTION_DOWN:觸摸摁下獲得相應的坐標.
MotionEvent.ACTION_MOVE:
裡面有個方法isNeedMove。作用:我們滑動的是ScrollView自身呢,還是我們自己模擬的那種滑動.
復制代碼 代碼如下:
/***
* 是不是須要挪動布局 inner.getMeasuredHeight():獲得的是控件的總高度
*
* getHeight():獲得的是屏幕的高度
*
* @return
*/
public void isNeedMove() {
int offset = inner.getMeasuredHeight() - getHeight();
int scrollY = getScrollY();
// 如果ScrollView的子View們沒有超越一屏幕則scrollY == 0,直接返回true,
//如果ScrollView的子View們超越了一屏幕則 getScrollY()==offset說明滑到了ScrollView的低端.這時候才返回true.
if (scrollY == 0 || scrollY == offset) {
isMoveing = true;
}
}
這裡面用到最多的就是:view.layout(l, t, r, b);作用很簡單不解釋。詳情請參看源碼.
MotionEvent.ACTION_UP:就是做些善後操作,主要看animation方法.
復制代碼 代碼如下:
/***
* 回縮動畫
*/
public void animation() {
TranslateAnimation image_Anim = new TranslateAnimation(0, 0,
Math.abs(initTop - current_Top), 0);
image_Anim.setDuration(200);
imageView.startAnimation(image_Anim);
imageView.layout(imageView.getLeft(), (int) initTop,
imageView.getRight(), (int) initBottom);
// 開啟挪動動畫
TranslateAnimation inner_Anim = new TranslateAnimation(0, 0,
inner.getTop(), normal.top);
inner_Anim.setDuration(200);
inner.startAnimation(inner_Anim);
inner.layout(normal.left, normal.top, normal.right, normal.bottom);
/** line_up **/
TranslateAnimation line_up_Anim = new TranslateAnimation(0, 0,
Math.abs(line_up_top - lineUp_current_Top), 0);
line_up_Anim.setDuration(200);
line_up.startAnimation(line_up_Anim);
line_up.layout(line_up.getLeft(), line_up_top, line_up.getRight(),
line_up_bottom);
normal.setEmpty();
/** 動畫執行 **/
if (current_Top > initTop + 50 && turnListener != null)
turnListener.onTurn();
}
這裡我要簡單說明一下,因為我在這裡栽了有些時光.
比如:我們的背景圖片本來坐標為:(0,-190,800,300),隨著手勢挪動到(0,-100,800,390)挪動了90像素,那麼我們的TranslateAnimation應當如何寫呢?我之前總以為不就是末尾坐標指向初始坐標不就完了,結果你會發明,動畫基本不起作用而是一閃而過。原因呢,動畫參數弗成以為正數.或許因為動畫是以(0,0)為參照物吧.因此要把動畫寫成TranslateAnimation line_up_Anim = new TranslateAnimation(0, 0,Math.abs(-190- (-100)), 0);這樣我們所須要的動畫效果就實現了.
但是新的問題又出現了:
當你下拉到必定狀態後然後漸漸向上挪動,會發明挪動的很快(沒有回縮的反響),而挪動到最頂部的時候突然又出現反彈效果。這個效果固然不是我們所須要的那種。我們所須要的效果是:下拉到必定水平,然後反過來上拉的時候要漸漸的挪動回到原點(中央位置)停止。如果是上拉的話,不要出現反彈效果,如果是下拉松開的話,出現反彈效果。
描述的有點亂,如果想知道具體效果的話,我提議你應用下papa,其實海內這些比擬優秀的應用UI都是抄襲國外的,如果你用facebook的話,就會發明,怎麼啪啪的個人頁面長的也忒像facebook了。請看下圖:
嘿嘿,不好意思,跑題了,針對上面出現的問題,我簡單說明一下.
首先,比如我們手勢下拉了50像素,其實是使得自定義ScrollView的孩子也就是LinearLayout這個控件的top為50,而這個時候的getScrollY()的值仍為0,但是如果此時你停止下拉反而向上拉取的話,那麼此時的getScrollY()會從0開始逐步增大,當我們挪動到頂部也就是將ScrollView挪動到最底部,此時的isMoveing為true,所以你繼承上拉的話會出現反彈效果。
這個問題要如何解決呢,其實也不難,但是我糾結了好長時光,也走了很多多少彎路。在這裡說明一下我的瞎跑路段以及疑難:當時我就想,getScrollY()這麼不聽話,我何必非要對ScrollView的孩子進行操作呢,為何直接不對本控件執行layout(l,t,r,b)呢,後來就照著這個邏輯進行update,終於更改了差不多了,糾結了問題再次出現,在你下拉的時候對ScrollView本身執行layout(l,t,r,b)這個方法可以實現反彈效果,但是此時你確無法進行滑動了,就是ScrollView本身的滑動無緣無故的被禁止掉了.我懷疑是layout的時候參數弄錯了。,後來仔細修改了下發明還是弗成以滑動,然後google了半天也杳無音訊,最後固然放棄,又回到了原點。接著揣摩。。。算是功夫不負有心人吧,最終想到了解決方案,希望對您有幫助。
還拿上面說到的那短話,比如我們手勢下拉了50像素,那麼此時touch的距離也就是50像素,如果此時我們反向上拉的話,同樣是須要50像素回到最初的位置。說到這裡我想大家都明確了。(首先我們要將操作離開,分為UP,DOWN,如果是DOWN的話,那麼在下拉後執行上拉的時候我們禁用掉自定義控件的滑動,而是通過手勢執行layout執行這50像素.)
上面我們看部分代碼:
復制代碼 代碼如下:
/**對於初次Touch操作要判斷方位:UP OR DOWN**/
if (deltaY < 0 && state == state.NOMAL) {
state = State.UP;
} else if (deltaY > 0 && state == state.NOMAL) {
state = State.DOWN;
}
if (state == State.UP) {
deltaY = deltaY < 0 ? deltaY : 0;
isMoveing = false;
shutTouch = false;
} else if (state == state.DOWN) {
if (getScrollY() <= deltaY) {
shutTouch = true;
isMoveing = true;
}
deltaY = deltaY < 0 ? 0 : deltaY;
}
代碼很簡單,不過多解釋了,不明確的話,仔細看下源碼確定就明確了。
touch 事件處理:
復制代碼 代碼如下:
/** touch 事件處理 **/
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (inner != null) {
commOnTouchEvent(ev);
}
// ture:禁止控件本身的滑動.
if (shutTouch)
return true;
else
return super.onTouchEvent(ev);
}
說明:如果返回值為true,作用:禁止ScrollView的滑動,此時的Touch事件還存哦!!!如果對Touch事件比擬熟悉的同窗,相信以為我有點空話了,哈哈,我也是個小菜鳥,也卡在這裡過。
最後呢,還有個小BUG,也就是那個頂部拉線,如果你讓ScrollView慣性滑動的話,那麼你會發明,頂部線條沒有追隨挪動,其實就是因為慣性滑動的時候我們是獲得不到getScrollY()的值得造成的,查了半天也沒有找到相關資料,這個問題就臨時就留在這裡,有時光了在續。
這裡我將源碼貼出來:
復制代碼 代碼如下:
package com.example.scrollviewdemo;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.ScrollView;
/**
* 自定義ScrollView
*
* @author jia
*
*/
public class PersonalScrollView extends ScrollView {
private final String TAG = PersonalScrollView.class.getSimpleName();
private View inner;// 孩子View
private float touchY;// 點擊時Y坐標
private float deltaY;// Y軸滑動的距離
private float initTouchY;// 初次點擊的Y坐標
private boolean shutTouch = false;// 是不是關閉ScrollView的滑動.
private Rect normal = new Rect();// 矩形(這裡只是個形式,只是用於判斷是不是須要動畫.)
private boolean isMoveing = false;// 是不是開始挪動.
private ImageView imageView;// 背景圖控件.
private View line_up;// 上線
private int line_up_top;// 上線的top
private int line_up_bottom;// 上線的bottom
private int initTop, initBottom;// 初始高度
private int current_Top, current_Bottom;// 拖動時時高度。
private int lineUp_current_Top, lineUp_current_Bottom;// 上線
private onTurnListener turnListener;
private ImageView imageHeader;
public void setImageHeader(ImageView imageHeader) {
this.imageHeader = imageHeader;
}
// 狀態:上部,下部,默認
private enum State {
UP, DOWN, NOMAL
};
// 默認狀態
private State state = State.NOMAL;
public void setTurnListener(onTurnListener turnListener) {
this.turnListener = turnListener;
}
public void setLine_up(View line_up) {
this.line_up = line_up;
}
// 注入背景圖
public void setImageView(ImageView imageView) {
this.imageView = imageView;
}
/***
* 構造方法
*
* @param context
* @param attrs
*/
public PersonalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
/***
* 根據 XML 生成視圖工作實現.該函數在生成視圖的最後調用,在所有子視圖添加完以後. 即使子類覆蓋了 onFinishInflate
* 方法,也應當調用父類的方法,使該方法得以執行.
*/
@Override
protected void onFinishInflate() {
if (getChildCount() > 0) {
inner = getChildAt(0);
}
}
/** touch 事件處理 **/
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (inner != null) {
commOnTouchEvent(ev);
}
// ture:禁止控件本身的滑動.
if (shutTouch)
return true;
else
return super.onTouchEvent(ev);
}
/***
* 觸摸事件
*
* @param ev
*/
public void commOnTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
initTouchY = ev.getY();
current_Top = initTop = imageView.getTop();
current_Bottom = initBottom = imageView.getBottom();
if (line_up_top == 0) {
lineUp_current_Top = line_up_top = line_up.getTop();
lineUp_current_Bottom = line_up_bottom = line_up.getBottom();
}
break;
case MotionEvent.ACTION_UP:
/** 回縮動畫 **/
if (isNeedAnimation()) {
animation();
}
if (getScrollY() == 0) {
state = State.NOMAL;
}
isMoveing = false;
touchY = 0;
shutTouch = false;
break;
/***
* 消除出第一次挪動計算,因為第一次無法得知deltaY的高度, 然而我們也要進行初始化,就是第一次挪動的時候讓滑動距離歸0.
* 以後記載精確了就正常執行.
*/
case MotionEvent.ACTION_MOVE:
touchY = ev.getY();
deltaY = touchY - initTouchY;// 滑動距離
/** 對於初次Touch操作要判斷方位:UP OR DOWN **/
if (deltaY < 0 && state == state.NOMAL) {
state = State.UP;
} else if (deltaY > 0 && state == state.NOMAL) {
state = State.DOWN;
}
if (state == State.UP) {
deltaY = deltaY < 0 ? deltaY : 0;
isMoveing = false;
shutTouch = false;
/** line_up **/
lineUp_current_Top = (int) (line_up_top - getScrollY());
lineUp_current_Bottom = (int) (line_up_bottom - getScrollY());
Log.e(TAG, "top=" + getScrollY());
line_up.layout(line_up.getLeft(), lineUp_current_Top,
line_up.getRight(), lineUp_current_Bottom);
} else if (state == state.DOWN) {
if (getScrollY() <= deltaY) {
shutTouch = true;
isMoveing = true;
}
deltaY = deltaY < 0 ? 0 : deltaY;
}
if (isMoveing) {
// 初始化頭部矩形
if (normal.isEmpty()) {
// 保存正常的布局位置
normal.set(inner.getLeft(), inner.getTop(),
inner.getRight(), inner.getBottom());
}
// 挪動布局(手勢挪動的1/3)
float inner_move_H = deltaY / 5;
inner.layout(normal.left, (int) (normal.top + inner_move_H),
normal.right, (int) (normal.bottom + inner_move_H));
/** image_bg **/
float image_move_H = deltaY / 10;
current_Top = (int) (initTop + image_move_H);
current_Bottom = (int) (initBottom + image_move_H);
imageView.layout(imageView.getLeft(), current_Top,
imageView.getRight(), current_Bottom);
/** line_up **/
lineUp_current_Top = (int) (line_up_top + inner_move_H);
lineUp_current_Bottom = (int) (line_up_bottom + inner_move_H);
line_up.layout(line_up.getLeft(), lineUp_current_Top,
line_up.getRight(), lineUp_current_Bottom);
}
break;
default:
break;
}
}
/***
* 回縮動畫
*/
public void animation() {
TranslateAnimation image_Anim = new TranslateAnimation(0, 0,
Math.abs(initTop - current_Top), 0);
image_Anim.setDuration(200);
imageView.startAnimation(image_Anim);
imageView.layout(imageView.getLeft(), (int) initTop,
imageView.getRight(), (int) initBottom);
// 開啟挪動動畫
TranslateAnimation inner_Anim = new TranslateAnimation(0, 0,
inner.getTop(), normal.top);
inner_Anim.setDuration(200);
inner.startAnimation(inner_Anim);
inner.layout(normal.left, normal.top, normal.right, normal.bottom);
/** line_up **/
TranslateAnimation line_up_Anim = new TranslateAnimation(0, 0,
Math.abs(line_up_top - lineUp_current_Top), 0);
line_up_Anim.setDuration(200);
line_up.startAnimation(line_up_Anim);
line_up.layout(line_up.getLeft(), line_up_top, line_up.getRight(),
line_up_bottom);
normal.setEmpty();
/** 動畫執行 **/
if (current_Top > initTop + 50 && turnListener != null)
turnListener.onTurn();
}
/** 是不是須要開啟動畫 **/
public boolean isNeedAnimation() {
return !normal.isEmpty();
}
/***
* 執行翻轉
*
* @author jia
*
*/
public interface onTurnListener {
/** 必須到達必定水平才執行 **/
void onTurn();
}
}
效果圖:
界面有點丑陋,不過UI可以自己根據需求進行調整.
react-native-easy-toast一款簡單易用的 Toast 組件,支持 Android&iOS。安裝1.在終端運行 npm i react-nati
如上圖所示,使用facebook sdk進行login和share的時候,需要新建android平台,用到key hashes,生成方法有兩種:方法一:keytool -
引言 程序猿們,是否還在為你的老板辛辛苦苦的打工而拿著微薄的薪水呢,還是不知道如何用自己的應用或游戲來賺錢呢! 在這裡IQuick將教您如何同過自己的應用來賺取自己的
新建項目,新建一個java類OtherScreenActivity 繼承自 Activity類package com.wuyudong.twoactivity;impor