博客爲 有時個哥 原創,如需轉載請標明出處:http://blog.csdn.net/ls703/article/details/41312921
前一段時間一個朋友說他們做應用,跟着蘋果的效果做,蘋果上有一個右滑退出頁面的效果,滑到一般不想退出可以滑回來的那種。他說他和自己的項目經理寫了一個,效果都不理想,我忙完自己的工作,有時間自己寫了寫,效果還算可以。一般這種頁面是需要自定義的。如果在一個activity裏面實現這種效果,是不算難的。但是我們不可能把所有的頁面都放在一個activity裏面,所以我就單獨寫了一個有這個效果的activity,假如我們想要用這種效果的,我們只要繼承這個activity即可,這個activity除了有右滑功能,其他的和正常的activity使用一樣。所以不會影響正常開發。下面就開始說一下具體思路。由於自己按自己的想法寫的,所以有不足之處還請大家指教。
首先,要實現這個效果需要自定義頁面,所以,我們首先先自定義一個佈局頁面,先曬代碼:
package com.example.slidingactivity.ui.weiget;
import com.example.slidingactivity.R;
import android.app.Activity;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.widget.RelativeLayout;
import android.widget.Scroller;
public class BaseRelativeLayout extends RelativeLayout {
private Activity activity;
int currentScreen = LEFT_SCREEN;
private static final int LEFT_SCREEN = 0;
private static final int RIGHT_SCREEN = 1;
private Scroller mScroller; // 專門用模擬數據.
public BaseRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, R.style.Transparent);
}
public BaseRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public BaseRelativeLayout(Context context) {
super(context);
this.activity = (Activity)context;
mScroller = new Scroller(context);
//獲得觸發滑動事件的最短距離
scaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
int oldX;
@Override
public boolean onTouchEvent(MotionEvent event) {
// System.out.println("width:"+ getWidth()+"height:"+getHeight());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
oldX = (int)event.getX();
break;
case MotionEvent.ACTION_UP:
if(getScrollX()<0&&getScrollX()>-getMeasuredWidth()/2){
currentScreen = LEFT_SCREEN;
}
if(getScrollX()<=-getMeasuredWidth()/2&&getScrollX()>=-getMeasuredWidth()){
currentScreen = RIGHT_SCREEN;
}
scrollScreen();
break;
case MotionEvent.ACTION_MOVE:
//移動後的座標
int moveX = (int)event.getX();
//偏移量
int deltaX = (oldX-moveX);
//滑動距離加上偏移量
int newScrollX = getScrollX() + deltaX;// 獲得當前屏幕x軸的偏移量
if(newScrollX>0){
scrollTo(0, 0);
}else if(newScrollX<=-getMeasuredWidth()){
scrollTo(-getMeasuredWidth(), 0);
activity.finish();
}else{
scrollBy(deltaX, 0);
}
oldX = moveX;
break;
default:
break;
}
return true;
}
/**
* 根據currentScreen變量來切換屏幕的顯示
*/
private void scrollScreen() {
int scrollX = getScrollX();
int dx = 0;
if(currentScreen == LEFT_SCREEN) {
// 向左滑動
dx = 0 - scrollX;
} else if(currentScreen == RIGHT_SCREEN) {
// 向右滑動
dx = -getChildAt(0).getMeasuredWidth() - scrollX;
}
// 開始模擬數據了.
mScroller.startScroll(scrollX, 0, dx, 0, Math.abs(dx) * 2);
// 刷新當前控件, 會引起當前控件的重繪.
invalidate(); // -> drawChild -> child.draw -> computeScroll()
}
/**
* 更新當前屏幕的x軸的值
* 如果mScroller類一直正在模擬數據. 想讓computeScroll方法一直被調用, mScroller停止模擬時停止調用此方法
*/
@Override
public void computeScroll() {
if(mScroller.computeScrollOffset()) { // true 代表當前正在模擬數據. false 已經停止模擬數據了
scrollTo(mScroller.getCurrX(), 0);
invalidate(); // 遞歸, 自己掉自己.
if(getScrollX()<=-getMeasuredWidth()){
activity.finish();
}
}
}
/**
* 在此攔截事件,左右滑動自身消費點事件
*/
private int scaledTouchSlop;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
oldX = (int)ev.getX();
break;
case MotionEvent.ACTION_MOVE:
int moveX = (int) ev.getX();
int diff = Math.abs(moveX - oldX);
if(diff > scaledTouchSlop) {
return true;
}
break;
default:
break;
}
return super.onInterceptTouchEvent(ev);
}
}
既然要實現滑動,那麼就需要重新寫ontouch方法,利用觸屏監聽的不同效果,先實現頁面能移動。然後根據位移量來實現移動佈局。上面代碼光劍地方也已做了註釋,上面就是自定義的一個可滑動的自定義佈局。實現自定義佈局後,那然後我們就開始想怎樣讓他添加在一個activity鍾,而不影響activity的正常使用。我們要的效果就是讓他和平常的activity用法一樣。直接繼承使用即可。所以我寫了一個基類BaseActivity一下是他的代碼。
package com.example.slidingactivity.base;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.TextView;
import com.example.slidingactivity.R;
import com.example.slidingactivity.ui.weiget.BaseRelativeLayout;
public class BaseActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setTheme(R.style.Transparent);
}
public void setContentView(View view) {
super.setContentView(getBaseView(view));
// setTheme(R.style.Transparent);
}
public void setContentView(int layoutResId) {
// setTheme(R.style.Transparent);
View view = View.inflate(this, layoutResId, null);
TextView tv = new TextView(this);
tv.setText("nihao");
super.setContentView(getBaseView(view));
}
private RelativeLayout getBaseView(View view) {
BaseRelativeLayout baseView = new BaseRelativeLayout(this);
view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
baseView.addView(view);
return baseView;
}
}
這就是整個類的代碼,看着代碼不多。既然要做到和普通的activity一樣,那麼佈局就是使用這給的,所以我這裏選擇,BaseActivity沒有自己靜態佈局的佈局,使用代碼動態添加使用繼承者提供的佈局。
爲了和普通的activity一樣,所以這裏我重寫了setContentView方法,這樣方法是我們熟悉的名字,就直接使用即可,通過重寫這個方法,來得到子類傳入的佈局,得到子類傳入的佈局,我們下一步就是讓子類的佈局和我們自定義的滑動佈局結合。其實思路是這樣的,把子類佈局嵌入自定義的華東佈局中。而滑動佈局就是BaseActivity的佈局。
這樣大體的完成了一大半了。沒錯就是這麼簡單。還有一個比較重要的一步。那就是給Activity設置透明風格。因爲是不同的Activity,在滑動的頁面的時候我們需要看到下面的Activity,所以要把Activity設置成透明的。這裏我也遇到一個問題。我沒有找到在代碼裏把整個Activity設置成透明的辦法。網上一些資料總是在工程裏實現不了那種效果。所以我是在xml設置style,在清單文件裏給Activity設置成透明的風格。
<style name="Transparent" parent="AppTheme">
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowBackground">@color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowAnimationStyle">@+android:style/Animation.Translucent</item>
</style>
在清單文件裏,
<activity android:name="com.example.slidingactivity.ContentActivity"
android:theme="@style/Transparent"
></activity>
如果想整個應用使用這種效果,那可以吧整個應用設置成透明的風格。
這樣就萬事大吉了。讓後我們寫幾個子類,繼承BaseActivity,
package com.example.slidingactivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import com.example.slidingactivity.base.BaseActivity;
public class ContentActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_content);
Button content = (Button)findViewById(R.id.content);
content.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// Toast.makeText(ContentActivity.this, "點擊了", 0).show();
Intent intent = new Intent(ContentActivity.this,ContentActivity1.class);
startActivity(intent);
}
});
}
}
}
MainActivity的代碼
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button tvClick = (Button) findViewById(R.id.tvClick);
tvClick.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Intent intent = new Intent(MainActivity.this,ContentActivity.class);
startActivity(intent);
}
});
}
}
下面是源碼:
http://download.csdn.net/detail/ls703/8179981
大家有什麼好的方法,或實現效果,可以一塊交流。
在最後,我也在這裏求大家指教,BaseActivity中把Activity變成透明的方法,也就是滑動的時候能看見下面的Activity。是求在代碼中實現。如果在代碼中能實現那種效果,那樣就不用麻煩在清單文件裏配置了。使用起來就會更加方便。
題外話:寫完之後聽朋友說,有這方面的架包,大家也可以去下載,在githup上面。具體地址我忘了,嘿嘿,大家可以自己找找。