1. 程式人生 > >寫一寫虎撲APP首頁效果,jrs都進來看一眼!

寫一寫虎撲APP首頁效果,jrs都進來看一眼!

首先先上效果圖:


首先我們先考慮這個介面有什麼難點:

1.日期懸停。

     2.今日和其他日期在懸停佈局上的處理

     3.首次進入時,自動滑動到今日比賽

     4.向上滑動向下滑動一定距離後,出現向上向下的圖示,並滾動到今日的比賽介面

     5.向上載入更多,向下載入更多,當然這個並不算是難點,只是比較新穎~。

好了,首先我們一項一項的開始解決這幾個難點,首先這樣的列表佈局,強烈推薦大家使用RecyclerView,谷歌粑粑真的是賦予了它無窮的潛力。觀看本文前依然推薦一波部落格地址:點選開啟連結 一個頭像狂魔的部落格。

如果你已經看過了上篇部落格,你可能會對這個介面已經有一定的瞭解,那麼首先我們實現ItemDecoration的編寫,首先我們應該考慮後臺與我們互動時給出來的資料,一般為List或者Map格式,所以我們在編寫時應充分考慮兩種格式寫法上的差異(其實也沒什麼差異的地方)。

public class HuPuItemDecoration<T extends HuPuBaseBean> extends RecyclerView.ItemDecoration {

    private int todayPosition;
    private List<T> mData;

    private Map<String, List<T>> mapData;

    private Paint mPaint;


    private int itemHeight;

    private int background;

    private int textSize;

    private int textColor;

    private Context context;

    private Rect outBounds = new Rect();


    public HuPuItemDecoration(Context context, int todayPosition, List<T> mData) {
        this.context = context;
        this.todayPosition = todayPosition;
        this.mData = mData;
        mPaint = new Paint();
        itemHeight = dip2px(context, 25);
        background = ContextCompat.getColor(context, android.R.color.darker_gray);
        textSize = sp2px(context, 15);
        textColor = ContextCompat.getColor(context, android.R.color.black);
    }

    public HuPuItemDecoration(Context context, int todayPosition, Map<String, List<T>> mapData) {
        this.context = context;
        this.todayPosition = todayPosition;
        this.mapData = mapData;
        mPaint = new Paint();
        itemHeight = dip2px(context, 25);
        background = ContextCompat.getColor(context, android.R.color.darker_gray);
        textSize = sp2px(context, 15);
        textColor = ContextCompat.getColor(context, android.R.color.black);
        for (Map.Entry<String, List<T>> entry : mapData.entrySet()) {
            mData.addAll(entry.getValue());
        }
    }
這裡的HuPuBaseBean是一個bean類,內部實現了getDayType的方法,這裡是基於封裝而寫出的基類,如果在實際專案中可以直接引入我們所需要的實體類來進行賦值。

初始化完成後,我們開始一步一步實現:程式碼很簡單,一梭子給出來一部分先:

關於這部分方法的意義,推薦大家去鴻洋sama的部落格去充值信仰!!!:點選開啟連結

 @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        int position = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
        if (null != mData && null != mapData) {
            throw new IllegalArgumentException("這裡不會走到的,只是判斷一下");
        }

        if (position > -1) {
            if (position == 0) {
                outRect.set(0, itemHeight, 0, 0);
            } else {
                if (!mData.get(position).getDayType().equals(mData.get(position - 1).getDayType())) {
                    outRect.set(0, itemHeight, 0, 0);
                } else {
                    outRect.set(0, 0, 0, 0);
                }
            }
        }
    }
這裡的程式碼邏輯比較簡單,就是當position==0時,我們必然會有一個divider去標識日期,而後當前的type不等於它所在位置的上一個position的type時,這時兩個position所在的view中間應有一個divider來進行標識!這部分程式碼非常的簡單,我們繼續一梭子擼下面的程式碼:
  @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        int left = parent.getPaddingLeft();
        int right = parent.getRight() - parent.getPaddingRight();
        int count = parent.getChildCount();
        for (int i = 0; i < count; i++) {
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
            int position = lp.getViewLayoutPosition();
            if (position == 0) {
                mPaint.setColor(background);
                c.drawRect(left, child.getTop() - itemHeight - lp.topMargin, right, child.getTop() - lp.topMargin, mPaint);
                mPaint.setColor(textColor);
                mPaint.setTextSize(textSize);
                String type = mData.get(position).getDayType();
                mPaint.getTextBounds(type, 0, type.length(), outBounds);
                c.drawText(type, child.getRight() / 2 - outBounds.width() / 2, child.getTop() - lp.topMargin -
 (itemHeight / 2 - outBounds.height() / 2), mPaint);
            } else {
                if (!mData.get(position).getDayType().equals(mData.get(position - 1).getDayType())) {
                    mPaint.setColor(background);
                    c.drawRect(left, child.getTop() - itemHeight - lp.topMargin, right, child.getTop() - lp.topMargin, mPaint);
                    if (position == todayPosition) {
                        mPaint.setColor(Color.RED);
                        c.drawRect(left, child.getTop() - itemHeight - lp.topMargin, 15, child.getTop() - lp.topMargin, mPaint);
                    }
                    mPaint.setColor(background);
                    mPaint.setTextSize(textSize);
                    mPaint.setColor(textColor);
                    String type = mData.get(position).getDayType();
                    mPaint.getTextBounds(type, 0, type.length(), outBounds);
                    c.drawText(type, child.getRight() / 2 - outBounds.width() / 2, child.getTop() -
 lp.topMargin - (itemHeight / 2 - outBounds.height() / 2), mPaint);
                } else {

                }
            }
        }

    }

這部分程式碼其實跟上面的程式碼在邏輯上非常接近,如果你已經看了最上面的部落格連結,這裡的邏輯你應該也能看得懂,這裡和推薦部落格在這裡並沒有什麼不同,如果這裡有任何疑問,我強烈大家去推薦部落格那裡去看詳細的解釋,(什麼是甩鍋,這就叫甩鍋)。

最後一段程式碼:

  @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
        int position = ((LinearLayoutManager) parent.getLayoutManager()).findFirstVisibleItemPosition();
        View itemView = parent.findViewHolderForAdapterPosition(position).itemView;
        if (mData.size() - 1 >= position) {
            if (!mData.get(position).getDayType().equals(mData.get(position + 1).getDayType())) {
                if (itemView.getHeight() + itemView.getTop() < itemHeight) {
                    c.translate(0, itemView.getHeight() + itemView.getTop() - itemHeight);

                }
            }

        }

        mPaint.setColor(background);
        c.drawRect(parent.getPaddingLeft(), parent.getPaddingTop(), parent.getRight() - parent.getPaddingRight(), 
parent.getPaddingTop() + itemHeight, mPaint);
        if (position >= todayPosition && mData.get(todayPosition).getDayType().equals(mData.get(position).getDayType())) {
            mPaint.setColor(Color.RED);
            c.drawRect(parent.getPaddingLeft(), parent.getPaddingTop(), 15, parent.getPaddingTop() + itemHeight, mPaint);
        }
        mPaint.setColor(textColor);
        mPaint.setTextSize(textSize);
        String dayType = mData.get(position).getDayType();
        mPaint.getTextBounds(dayType, 0, dayType.length(), outBounds);
        c.drawText(dayType, (parent.getRight() - parent.getPaddingRight() - parent.getPaddingLeft() - outBounds.width()) / 2,
 parent.getPaddingTop() + itemHeight - (itemHeight / 2 - outBounds.height() / 2), mPaint);

    }

這裡就是做懸停部分的程式碼,這裡唯一需要注意的就是我們對於今日比賽懸停介面的處理。
 if (position >= todayPosition && mData.get(todayPosition).getDayType().equals(mData.get(position).getDayType())) {
            mPaint.setColor(Color.RED);
            c.drawRect(parent.getPaddingLeft(), parent.getPaddingTop(), 15, parent.getPaddingTop() + itemHeight, mPaint);
        }
如果當前的position大於等於我們傳過來的todayPos,然後type相等,既判定為今日比賽。其他則無視之。

寫完了這部分程式碼後,我們就簡單寫一個RecyclerView出來:

 rv = (RecyclerView) findViewById(R.id.rv);
        top= (ImageView) findViewById(R.id.top);
        bottom= (ImageView) findViewById(R.id.bottom);

        for (int i = 0; i < 10; i++) {
            HuPuBean bean = new HuPuBean("雷霆", "火箭", "早上8:00", 124, 100, "昨日", false);
            mData.add(bean);
        }
        for (int i = 0; i < 10; i++) {
            HuPuBean bean = new HuPuBean("雷霆", "火箭", "早上8:00", 124, 100, "今日", true);
            mData.add(bean);
        }
        for (int i = 0; i < 10; i++) {
            HuPuBean bean = new HuPuBean("雷霆", "火箭", "早上8:00", 124, 100, "明日", true);
            mData.add(bean);
        }

        rv.addItemDecoration(new HuPuItemDecoration<HuPuBean>(MainActivity.this, Utils.getTodayPosition("今日", mData), mData));
        final LinearLayoutManager manger = new LinearLayoutManager(MainActivity.this, LinearLayoutManager.VERTICAL, false);
        rv.setLayoutManager(manger);
        rv.setAdapter(new RecyclerView.Adapter<Holder>() {
            @Override
            public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
                View itemView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item, parent, false);
                return new Holder(itemView);
            }

            @Override
            public void onBindViewHolder(Holder holder, int position) {
                HuPuBean bean = mData.get(position);
                holder.teamOne.setText(bean.getTeamOne());
                holder.teamTwo.setText(bean.getTeamTwo());
                holder.teamOnePoint.setText("" + bean.getTeamOnePoint());
                holder.teamOnePoint.setText("" + bean.getTeamTwoPoint());
            }

            @Override
            public int getItemCount() {
                return mData.size();
            }
        });
這裡程式碼寫得比較粗糙,勿怪。

然後我們來看一下效果圖。



我們來看一下utils方法,也非常簡單,因為涉及到載入重新整理的邏輯,todayPostion的位置會發生改變,不建議使用賦值,

public class Utils {


    public static int getTodayPosition(String type, List<? extends HuPuBaseBean> mData) {
        for (int i = 0; i < mData.size(); i++) {
            if (mData.get(i).getDayType().equals(type)) {
                return i;
            }
        }
        return 0;
    }


}

最後我們來處理上滑下滑時top bottom圖片的顯示隱藏:

首先我們看MianActivity的佈局:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <android.support.v7.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/rv"
        ></android.support.v7.widget.RecyclerView>

    <ImageView
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_gravity="right"
        android:layout_marginTop="20dp"
        android:layout_marginRight="20dp"
        android:id="@+id/bottom"
        android:background="@mipmap/ic_launcher"
        android:visibility="gone"
        />
    <ImageView
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_gravity="bottom|right"
        android:layout_marginBottom="20dp"
        android:layout_marginRight="20dp"
        android:id="@+id/top"
        android:background="@mipmap/ic_launcher"
        android:visibility="gone"
        />
</FrameLayout>
然後通過RecyclerView的對滑動監聽來實現兩張圖片的顯示和隱藏:
 rv.addOnScrollListener(new RecyclerView.OnScrollListener() {

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                int position = manger.findFirstVisibleItemPosition();
                if(position-Utils.getTodayPosition("今日",mData)>=10){
                    bottom.setVisibility(View.GONE);
                    top.setVisibility(View.VISIBLE);
                }else if(position-Utils.getTodayPosition("今日",mData)<=-10){
                    bottom.setVisibility(View.VISIBLE);
                    top.setVisibility(View.GONE);
                }else {
                    bottom.setVisibility(View.GONE);
                    top.setVisibility(View.GONE);
                }
            }
        });

當然這種實現方式是最常見的,不過我們還可以通過自定義behavior這種方式來實現滑動的聯動效果,但是本篇就不在這裡實現了。

最後首次載入滑動到今日比賽,和點選圖片滑動到今日比賽,其實非常簡單,一行程式碼搞定:

  manger.scrollToPositionWithOffset(Utils.getTodayPosition("今日", mData), 0);
至此,基本的虎撲看球頁面的效果就已經實現了,是不是非常簡單,當然虎撲的頁面更加複雜,itemType有數種,這裡就不再實現了。最後去愉快的水群了。臨走前在發一張我老婆的美圖: