編輯:關於Android編程
最近在玩一個叫“約會吧”的應用,也是在看直播app,默認下載安裝的,安裝點進去看這個應用做的不錯,就留下來了。然後看他們動態詳情頁底部有一個效果:Recyclerview滑動到的評論列表的時候,底部點贊那欄會往左滑動,出現一個輸入評論的欄;然後下拉到底部的時候輸入評論欄會往右滑動,出現點贊欄。詳細細節直接來看效果圖吧。

其實這種效果現在在應用中還是很常見的,有上拉,toolbar、底部view隱藏,下拉顯示,或者像現在約會吧這樣左右滑動的效果。而且網上資料現在也有很多,有通過ObjectAnimation來實現的,這裡我們通過另外一種方法來實現。仔細下看下這個效果,其實他就是view滾動的效果,想到Android裡面的滾動,馬上就能想到scroller類了,scroller有一個startScroll()方法,通過這個方法我們就可以滾動了。滾動問題解決了,那麼這個效果就很簡單了,進入頁面時,把要顯示view的先顯示出來,不該顯示的暫時放在屏幕外面,當滾動的時間,我們控制view進入屏幕或者退出屏幕。大概思路就是這樣,下面我們就來實現這樣的效果吧。
效果的實現
首先,我們根據上面的思路把布局給整出來。結構如下圖:

這裡說明下上面的圖,分為3塊來說,
- 當Recyclerview上拉的時候,屏幕內5位置的view會隱藏,也就是移動到屏幕外面的6位置,當Recyclerview下拉的時候,屏幕外面的6位置view又會回到5位置顯示。
- 當Recyclerview上拉的時候,屏幕內的1位置的view會隱藏,也就是移動到屏幕外面的4位置,當Recyclerview下拉的時候,屏幕外面的4位置view會回到1位置顯示。
- 當RecyclerView上拉的時候,而且設置為水平方向左右滑動的時候,屏幕內的1位置的view會移動到3位置,同時屏幕外面2位置view會移動到屏幕內1位置來顯示,當RecyclerView下拉的時候,屏幕外的3位置會移動到屏幕內的1位置。1位置顯示的view也會回到屏幕外的2位置隱藏。這也就是上面應用的效果。
布局效果和代碼如下(這裡添加兩個按鈕來切換底部方向的效果):

效果圖
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/white"> <android.support.v7.widget.RecyclerView android:id="@+id/id_recyclerview" android:layout_width="match_parent" android:layout_height="match_parent" /> <RelativeLayout android:id="@+id/id_horization_rl" android:layout_width="match_parent" android:layout_height="60dp" android:layout_alignParentBottom="true" android:orientation="horizontal" > <TextView android:id="@+id/id_bottom_float" android:layout_width="match_parent" android:layout_height="60dp" android:text="我是點贊操作布局" android:textSize="18sp" android:gravity="center" android:background="#E2E2E2"> </TextView> <TextView android:id="@+id/id_bottom_comment" android:layout_width="match_parent" android:layout_height="60dp" android:text="我是評論輸入布局" android:textSize="18sp" android:gravity="center" android:background="#FF4500"> </TextView> </RelativeLayout> <TextView android:id="@+id/id_bottom_vertical" android:layout_width="match_parent" android:layout_height="60dp" android:text="你滑動,我隨你而變" android:layout_alignParentBottom="true" android:background="#eeeeee" android:gravity="center" android:textSize="16sp" /> <TextView android:id="@+id/id_top_vertical" android:layout_width="match_parent" android:layout_height="60dp" android:text="你滑動,我隨你而變" android:background="#eeeeee" android:gravity="center" android:textSize="16sp" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/id_switch" android:orientation="vertical" android:layout_alignParentRight="true" android:layout_centerVertical="true"> <TextView android:layout_width="wrap_content" android:layout_height="60dp" android:gravity="center" android:background="#eeeeee" android:text="切換底部水平動畫" android:onClick="showHorization"/> <TextView android:layout_width="wrap_content" android:layout_height="60dp" android:gravity="center" android:background="#eeeeee" android:onClick="showVertical" android:layout_marginTop="10dp" android:text="切換底部垂直動畫"/> </LinearLayout> </RelativeLayout>
然後,我們再寫一個線程來實現滾動的效果。代碼如下:
public class AnimationUtil implements Runnable{
private Context mContext;
//傳入需要操作的view
private View mAnimationView;
//view的寬和高
private int mViewWidth;
private int mViewHeight;
//動畫執行時間
private final int DURATION = 400;
//是水平還是垂直滑動變化
public boolean mOrientaion ;
//滾動操作類
private Scroller mScroller;
private boolean isShow;
public AnimationUtil(Context context,final View mAnimationView){
this.mContext = context ;
this.mAnimationView = mAnimationView ;
mScroller = new Scroller(context,new LinearInterpolator());
//水平布局這裡以屏幕寬為准
mViewWidth = getScreenWidth();
mViewHeight = mAnimationView.getMeasuredHeight();
if(mViewHeight==0){
mAnimationView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
mAnimationView.getViewTreeObserver().removeOnPreDrawListener(this);
mViewHeight = mAnimationView.getMeasuredHeight();
return true;
}
});
}
}
public void setOrientaion(boolean isHorization){
this.mOrientaion = isHorization;
}
//根據滑動變化,isScrollUp為true水平左邊滑動,否則反之,
//為false垂直往下隱藏,否則反之,
public void startHideAnimation(boolean isScrollUp){
isShow = false ;
if(!mOrientaion){
int dy = (int) (mAnimationView.getTranslationY()+mViewHeight);
if(!isScrollUp){
dy = (int)(mAnimationView.getTranslationY() - mViewHeight);
}
dy = cling(-mViewHeight,mViewHeight,dy);
mScroller.startScroll(0, (int) mAnimationView.getTranslationY(),0,dy,DURATION);
ViewCompat.postOnAnimation(mAnimationView,this);
return;
}
int dx = (int) (mAnimationView.getTranslationX()-mViewWidth);
if(!isScrollUp){
dx = (int)(mAnimationView.getTranslationX() + mViewWidth);
}
dx = cling(-mViewWidth,mViewWidth,dx);
mScroller.startScroll((int)mAnimationView.getTranslationX(),0,dx,0,DURATION);
ViewCompat.postOnAnimation(mAnimationView,this);
}
//顯示控件
public void startShowAnimation(){
isShow = true ;
if(!mOrientaion){
int dy = (int) ViewCompat.getTranslationY(mAnimationView);
dy = cling(-mViewHeight,mViewHeight,dy);
mScroller.startScroll(0,dy,0,-dy,DURATION);
ViewCompat.postOnAnimation(mAnimationView,this);
return;
}
int dx = (int) ViewCompat.getTranslationX(mAnimationView);
dx = cling(-mViewWidth,mViewWidth,dx);
mScroller.startScroll(dx,0,-dx,0,DURATION);
ViewCompat.postOnAnimation(mAnimationView,this);
}
//判斷當前綁定動畫控件是否顯示,
public boolean isShow() {
return isShow;
}
//終止動畫
public void abortAnimation(){
mScroller.abortAnimation();
}
@Override
public void run() {
if(mScroller.computeScrollOffset()){
//動畫沒停止就繼續滑動
ViewCompat.postOnAnimation(mAnimationView,this);
if(!mOrientaion){
ViewCompat.setTranslationY(mAnimationView,mScroller.getCurrY());
return;
}
ViewCompat.setTranslationX(mAnimationView,mScroller.getCurrX());
}
}
public int getScreenWidth(){
WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(dm);
return dm.widthPixels;
}
//控制在一個范圍的值
public int cling(int min,int max,int value){
return Math.min(Math.max(min, value), max);
}
}
這裡簡單說下上面AnimationUtil線程,首先它會創建一個滾動操作類Scroller,然後獲取需要滾動的view的寬和高的獲取,這裡寬直接取屏幕的寬度。同時還有一個mOrientaion屬性,方向的控制。然後startHideAnimation和startShowAnimation兩個方法。其中startHideAnimation中,我們計算出每個效果的初始位置的x和y。然後x和y軸移動的偏移量,然後startScroll方法的調用,然後把通過ViewCompat.postOnAnimation把移動動畫綁定在傳入的view裡面。startShowAnimation方法也是同理。我們知道,調用了startScroll,只是告訴Scroller移動到什麼位置,具體的移動信息是在computeScrollOffset獲取。所以我們通過這個方法就去判斷view是否移動完成,沒有移動,繼續調用當前線程,同時根據方向設置setTranslationY或者setTranslationX。
view滾動的幫助類實現完了,我們就寫個Recyclerview來簡單的測試下,MainActivity代碼如下:
public class MainActivity extends AppCompatActivity {
//通過recyclerview來提供滑動事件
private RecyclerView mRecyclerView;
//一些簡單的測試數據
private TestAdapter mRecyclerAdapter;
//水平簡單贊布局view綁定動畫
private AnimationUtil mZanAnimationUtil;
//水平簡單評論布局view綁定動畫
private AnimationUtil mCommAnimationUtil;
//垂直底部view綁定動畫
private AnimationUtil mBottomVerticalUtil;
//垂直頭頂view綁定布局
private AnimationUtil mTopVerticalUtil;
private List<String> mDataList=Arrays.asList("對Ta說了悄悄話","沖哥","小歡","對象,你在哪","暖心男神","一次就好",
"對Ta說了悄悄話","沖哥","小歡","對象,你在哪","暖心男神","一次就好",
"對Ta說了悄悄話","沖哥","小歡","對象,你在哪","暖心男神","一次就好",
"對Ta說了悄悄話","沖哥","小歡","對象,你在哪","暖心男神","一次就好");
private LinearLayoutManager mRecyclerManager;
//贊布局控件
private TextView mZanTextView;
//評論布局控件
private TextView mCommentView;
private RelativeLayout mHorizationalRl;
//底部布局控件
private TextView mVerticalBottomTv;
//頭部布局控件
private TextView mVerticalTopTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.id_recyclerview);
mZanTextView = (TextView) findViewById(R.id.id_bottom_float);
mCommentView = (TextView)findViewById(R.id.id_bottom_comment) ;
mVerticalBottomTv = (TextView)findViewById(R.id.id_bottom_vertical);
mHorizationalRl = (RelativeLayout)findViewById(R.id.id_horization_rl) ;
mVerticalTopTv = (TextView)findViewById(R.id.id_top_vertical);
mZanAnimationUtil = new AnimationUtil(this,mZanTextView);
mCommAnimationUtil = new AnimationUtil(this,mCommentView);
mBottomVerticalUtil = new AnimationUtil(this,mVerticalBottomTv);
mTopVerticalUtil = new AnimationUtil(this,mVerticalTopTv);
mZanAnimationUtil.setOrientaion(true);
mCommAnimationUtil.setOrientaion(true);
mCommAnimationUtil.startHideAnimation(false);
mHorizationalRl.setVisibility(View.GONE);
mRecyclerManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mRecyclerManager);
mRecyclerAdapter = new TestAdapter(mDataList,this);
mRecyclerView.setAdapter(mRecyclerAdapter);
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
//當滑動停止時動畫開始
if(newState == RecyclerView.SCROLL_STATE_IDLE){
//在到達某個item改變水平布局
if(mRecyclerManager.findFirstVisibleItemPosition()>4){
mZanAnimationUtil.startHideAnimation(true);
mCommAnimationUtil.startShowAnimation();
}else{
mZanAnimationUtil.startShowAnimation();
if(mCommAnimationUtil.isShow()){
mCommAnimationUtil.startHideAnimation(false);
}
}
//頭部和底部動畫操作
if(mRecyclerManager.findFirstVisibleItemPosition()>0){
mBottomVerticalUtil.startHideAnimation(true);
mTopVerticalUtil.startHideAnimation(false);
}else{
mBottomVerticalUtil.startShowAnimation();
mTopVerticalUtil.startShowAnimation();
}
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
}
});
}
public void showVertical(View view){
mHorizationalRl.setVisibility(View.GONE);
mVerticalBottomTv.setVisibility(View.VISIBLE);
}
public void showHorization(View view){
mHorizationalRl.setVisibility(View.VISIBLE);
mVerticalBottomTv.setVisibility(View.GONE);
}
}
主要是onScrollStateChanged方法裡面的操作。主要就是注意下評論布局控件的初始化就好了。
再貼下其他的類
TestAdapter.class
public class TestAdapter extends RecyclerView.Adapter<TestAdapter.SimpleViewHolder>{
private List<String> mDataList;
private Context mContext;
private LayoutInflater mInflater;
public TestAdapter(List<String> mDataList, Context mContext) {
this.mDataList = mDataList;
this.mContext = mContext;
mInflater = LayoutInflater.from(mContext);
}
@Override
public SimpleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new SimpleViewHolder(mInflater.inflate(R.layout.simple_item,parent,false));
}
@Override
public void onBindViewHolder(SimpleViewHolder holder, int position) {
holder.mTextView.setText(mDataList.get(position));
}
@Override
public int getItemCount() {
return mDataList.size();
}
public class SimpleViewHolder extends RecyclerView.ViewHolder{
private TextView mTextView;
public SimpleViewHolder(View itemView) {
super(itemView);
this.mTextView = (TextView)itemView.findViewById(R.id.id_text);
}
}
}
simple_item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/white"> <LinearLayout android:layout_width="match_parent" android:layout_height="100dp" android:orientation="horizontal" android:gravity="center_vertical"> <ImageView android:layout_width="60dp" android:layout_height="60dp" android:background="#EEEEEE" android:layout_margin="10dp" android:src="@drawable/post_default_avatar"/> <TextView android:id="@+id/id_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="21111111" android:textSize="14sp" android:layout_marginLeft="10dp"/> </LinearLayout> <View android:layout_width="match_parent" android:layout_height="0.5dp" android:layout_marginTop="10dp" android:background="#eeeeee"/> </LinearLayout>
最後,看下實現的效果:

這裡 開發環境為android studio 2.1.0 -preview4
源碼下載:Recyclerview滑動左右移動
以上就是本文的全部內容,希望對大家學習Android軟件編程有所幫助。
手機QQ快速取消圖標上未讀消息的數字
手機QQ快速取消圖標上未讀消息的數字。現在手機QQ5.0也開始跟iso的一樣。在圖表顯示未讀消息的數字。我們進入手機QQ的時候,想取消那個未讀的顯示數字,就
如何用Toolbar取代ActionBar
在 Android 3.0 開始,App Bar的功能逐漸被加入到 ActionBar中,而這個 ActionBar 被包含在 Theme 中,不過對於開發者來說,缺乏的
Android實用控件自定義逼真相機光圈View
最近手機界開始流行雙攝像頭,大光圈功能也應用而生。所謂大光圈功能就是能夠對照片進行後期重新對焦,其實現的原理主要是對拍照期間獲取的深度圖片與對焦無窮遠的圖像通過算法來實現
Android學習-新聞客戶端養成記(一)
經過數日的努力,新聞客戶端終於完成了,想用博客記錄一下開發過程中遇到的問題和解決方法,以免以後遇到同樣的問題罵自己記性差.項目的前期准備做項目前應該仔細的規劃一下功能需求
Android項目編譯的時候出現:Caused by: java.lang.ClassNotFoundException: com.example.aaa.MainActivity
錯誤類型: 04-28 06:10:15.508: E/Andro