編輯:關於Android編程
1、實現列表頭部的圖片輪播,方式:給RecyclerView添加HeaderView。
RecyclerView默認是沒有添加HeaderView方法的,所以我從網上找到了一種從外部添加View的方式加以實現。 要給RecyclerView添加HeaderView,首先要准備一個View:xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/rpv_news_detail" android:layout_width="match_parent" android:layout_height="180dp" app:rollviewpager_play_delay="3000" />
這是一個開源的圖片輪播控件,可以實現圖片的循環輪播。在github上可以找到。用法可以到這裡看說明。本節只是說明怎樣給RecyclerView添加HeaderView。 HeaderView已經有了,接著要在RecyclerView的Adapter定義一個方法:
// headerView的setter public void setHeaderView(View headerView) { this.headerView = headerView; notifyItemInserted(0); }
上面的方法接收一個View參數,賦值給Adapter的成員變量headerView,然後調用notify方法通知RecyclerView在0號位置添加了一個Item。這樣就可以達到添加HeaderView的效果。 此方法的調用時機實在給RecyclerView設置Adapter之前,也就是在調用setAdapter方法之前。 到這裡並沒有結束,我們還要繼續在Adapter類裡操作。首先,現在在RecyclerView裡已經不止一種類型的View,所以我們要復寫getItemViewType(int position)方法,在裡面進行View的區分。
@Override public int getItemViewType(int position) { if (headerView == null) return NORMAL; if (position == 0) return HEADER; return NORMAL; }上面的方法很好理解,如果headerView為空則返回普通View,如果在位置0處,則返回Header,其他情況都返回普通View,其中的NORMAL和HEADER是兩個自定義的整型常量,用於區分View,可以是任意不同的整型值。 接著,在onCreateViewHolder方法中通過viewTyper的判斷,做不同的操作。
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (headerView != null && viewType == HEADER) return new ViewHolder(headerView); View view = LayoutInflater.from(mContext).inflate(R.layout.item_news_detail_rv, parent, false); return new ViewHolder(view); } }
如果是HEADER,則將headerView傳進去,否則,正常的View傳進去。ViewHolder:
public class ViewHolder extends RecyclerView.ViewHolder { private ImageView iv_item_news_detail_rv; private TextView tv_title; private TextView tv_author; private TextView tv_zan; public ViewHolder(View itemView) { super(itemView); if (itemView == headerView) return; iv_item_news_detail_rv = (ImageView) itemView.findViewById(R.id.iv_item_news_detail_rv); tv_title = (TextView) itemView.findViewById(R.id.tv_title_news_detail); tv_author = (TextView) itemView.findViewById(R.id.tv_author_news_detail); tv_zan = (TextView) itemView.findViewById(R.id.tv_zan_news_detail); } }
可以看到在ViewHolder的構造函數裡,判斷itemView的種類,如果是headerView,則直接返回,如果不是,則獲取普通布局裡控件的引用。 最後,在onBindViewHolder中:
@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) { if (getItemViewType(position) == HEADER) return; final int pos = getRealPosition(holder); NewsModel currNM = mList.get(pos); ViewHolderNormal normalHolder = (ViewHolderNormal) holder; normalHolder.tv_title.setText(currNM.title); normalHolder.tv_author.setText(currNM.source); normalHolder.tv_zan.setText(currNM.replyCount + "跟帖"); Glide.with(mContext).load(currNM.imgsrc ).into(normalHolder.iv_item_news_detail_rv); }
如果是HEADER,則直接返回,如果不是,則填充數據。其中有一句
final int pos = getRealPosition(holder);因為添加了一個headerView,其他的Item的position都往後移了以為,所以要進行動態計算:
private int getRealPosition(RecyclerView.ViewHolder holder) { int position = holder.getLayoutPosition(); return headerView == null ? position : position - 1; }
至此,就完成了對RecyclerView的HeaderView的添加。
2、在添加HeaderView的基礎上,實現RecyclerView加載兩種不同布局的Item。
先准備好兩種布局:android:layout_width="match_parent" android:layout_height="110dp" android:orientation="horizontal"> android:id="@+id/iv_item_news_detail_rv" android:layout_width="120dp" android:layout_height="110dp" android:layout_marginLeft="10dp" android:src="@mipmap/i1" /> android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1"> android:id="@+id/tv_title_news_detail" android:layout_width="match_parent" android:layout_height="80dp" android:layout_marginLeft="3dp" android:lineSpacingExtra="10dp" android:lines="2" android:paddingLeft="5dp" android:paddingTop="10dp" android:text="Snapshots of the development version are available in Sonatype's snapshots repository." android:textColor="@android:color/black" android:textSize="18sp" /> android:id="@+id/tv_author_news_detail" android:layout_width="wrap_content" android:layout_height="30dp" android:layout_below="@+id/tv_title_news_detail" android:layout_marginLeft="3dp" android:paddingLeft="5dp" android:text="New Text" android:textColor="@android:color/darker_gray" android:textSize="15sp" /> android:id="@+id/tv_zan_news_detail" android:layout_width="match_parent" android:layout_height="30dp" android:layout_below="@+id/tv_title_news_detail" android:layout_marginLeft="3dp" android:layout_toRightOf="@+id/tv_author_news_detail" android:gravity="right" android:paddingLeft="5dp" android:text="New Text" android:textColor="@android:color/darker_gray" android:textSize="15sp" />效果圖:
android:layout_width="match_parent" android:layout_height="180dp" android:orientation="vertical"> android:id="@+id/tv_title_item_image" android:layout_width="match_parent" android:layout_height="40dp" android:padding="10dp" android:text="標題" android:textColor="@android:color/black" android:textSize="17sp" /> android:layout_width="match_parent" android:layout_height="110dp"> android:id="@+id/iv_left_item_image" android:layout_width="0dp" android:layout_height="match_parent" android:layout_marginLeft="10dp" android:layout_weight="1" android:src="@mipmap/i1" /> android:id="@+id/iv_center_item_image" android:layout_width="0dp" android:layout_height="match_parent" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:layout_weight="1" android:src="@mipmap/i1" /> android:id="@+id/iv_right_item_image" android:layout_width="0dp" android:layout_height="match_parent" android:layout_marginRight="10dp" android:layout_weight="1" android:src="@mipmap/i1" /> android:layout_width="match_parent" android:layout_height="30dp"> android:id="@+id/tv_source_item_image" android:layout_width="wrap_content" android:layout_height="30dp" android:paddingLeft="10dp" android:text="要聞" android:textColor="@android:color/darker_gray" android:textSize="16sp" /> android:id="@+id/tv_reply_item_image" android:layout_width="match_parent" android:layout_height="30dp" android:gravity="right" android:paddingRight="10dp" android:text="跟帖" android:textColor="@android:color/darker_gray" android:textSize="16sp" />
效果圖: 在Adapter創建兩個對應的ViewHolder類:
public class ViewHolderNormal extends RecyclerView.ViewHolder { private ImageView iv_item_news_detail_rv; private TextView tv_title; private TextView tv_author; private TextView tv_zan; public ViewHolderNormal(View itemView) { super(itemView); if (itemView == headerView) return; iv_item_news_detail_rv = (ImageView) itemView.findViewById(R.id.iv_item_news_detail_rv); tv_title = (TextView) itemView.findViewById(R.id.tv_title_news_detail); tv_author = (TextView) itemView.findViewById(R.id.tv_author_news_detail); tv_zan = (TextView) itemView.findViewById(R.id.tv_zan_news_detail); } } public class ViewHolderMultiImage extends RecyclerView.ViewHolder { private ImageView iv_center; private ImageView iv_right; private ImageView iv_item_news_detail_rv; private TextView tv_title; private TextView tv_author; private TextView tv_zan; public ViewHolderMultiImage(View itemView) { super(itemView); iv_item_news_detail_rv = (ImageView) itemView.findViewById(R.id.iv_left_item_image); iv_center = (ImageView) itemView.findViewById(R.id.iv_center_item_image); iv_right = (ImageView) itemView.findViewById(R.id.iv_right_item_image); tv_title = (TextView) itemView.findViewById(R.id.tv_title_item_image); tv_author = (TextView) itemView.findViewById(R.id.tv_source_item_image); tv_zan = (TextView) itemView.findViewById(R.id.tv_reply_item_image); } }
現在由於是三種ViewType,所以修改getItemViewType:
@Override public int getItemViewType(int position) { if (headerView == null) { NewsModel currNM = mList.get(position - 1); if (currNM.imgextra == null || currNM.imgextra.size() == 0) return NORMAL; return MULTIIMAGE; } if (position == 0) return HEADER; NewsModel currNM = mList.get(position - 1); if (currNM.imgextra == null || currNM.imgextra.size() == 0) return NORMAL; return MULTIIMAGE; }
在不是headerView的情況下,根據所獲取的新聞是圖片新聞還是普通新聞,返回對應的ViewType。 在onCreateViewHolder也要增加一些判斷:
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (headerView != null && viewType == HEADER) return new ViewHolderNormal(headerView); if (viewType == NORMAL) { View view = LayoutInflater.from(mContext).inflate(R.layout.item_news_detail_rv, parent, false); return new ViewHolderNormal(view); } View view = LayoutInflater.from(mContext).inflate(R.layout.item_image_news_detail_rv, parent, false); return new ViewHolderMultiImage(view); }
最後在onBindViewHolder中:
@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) { if (getItemViewType(position) == HEADER) return; final int pos = getRealPosition(holder); NewsModel currNM = mList.get(pos); switch (getItemViewType(position)) { case NORMAL: ViewHolderNormal normalHolder = (ViewHolderNormal) holder; normalHolder.tv_title.setText(currNM.title); normalHolder.tv_author.setText(currNM.source); normalHolder.tv_zan.setText(currNM.replyCount + "跟帖"); Glide.with(mContext).load(currNM.imgsrc).into(normalHolder.iv_item_news_detail_rv); break; case MULTIIMAGE: ViewHolderMultiImage imageHolder = (ViewHolderMultiImage) holder; imageHolder.tv_title.setText(currNM.title); imageHolder.tv_author.setText(currNM.source); imageHolder.tv_zan.setText(currNM.replyCount + "跟帖"); Glide.with(mContext).load(currNM.imgsrc).into(imageHolder.iv_item_news_detail_rv); if (currNM.imgextra != null && currNM.imgextra.size() > 1) { Glide.with(mContext).load(currNM.imgextra.get(0).imgsrc).into(imageHolder.iv_center); Glide.with(mContext).load(currNM.imgextra.get(1).imgsrc).into(imageHolder.iv_right); } break; } }
代碼都很簡單。這樣就實現了RecyclerView加載兩種不同ViewType的Item。
3、實現下拉刷新。
這裡我用的是android的v4包裡的SwipeRefreshLayout。首先布局文件中,讓SwipeRefreshLayout包裹RecyclerView:android:id="@+id/srl_news_detail" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="vertical" android:visibility="gone"> android:id="@+id/rv_news_detail" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone" />
接著在頁面中獲取SwipeRefreshLayout的引用,並給其設置一些參數,如刷新進度條的顏色、背景顏色、位置:
// 背景顏色 srl_news_detail.setProgressBackgroundColorSchemeResource(android.R.color.white); // 進度條變換顏色 srl_news_detail.setColorSchemeResources(android.R.color.holo_blue_light, android.R.color.holo_red_light, android.R.color.holo_orange_light, android.R.color.holo_green_light); // 進度條顯示位置 srl_news_detail.setProgressViewOffset(false, 0, (int) TypedValue .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, getResources() .getDisplayMetrics()));
最後給SwipeRefreshLayout設置onRefreshListener:
srl_news_detail.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { page = 0; mPresenter.loadRefreshData(getUrl()); } });
其中的
mPresenter.loadRefreshData(getUrl());
就是做的加載數據並更新頁面的操作。
// 監聽RecyclerView滑動狀態,如果滑動到最後,實現上拉刷新 rv_news_detail.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); if (newState == RecyclerView.SCROLL_STATE_IDLE && lastVisibleItem + 1 == adapter.getItemCount()) { if (adapter.getItemCount() < 360) { adapter.changeLoadStatus(NewsDetailAdapter.LOADING); page += 10; LogUtils.e("NewsDetailPresenter", getUrl()); mPresenter.loadMoreData(getUrl()); } else { adapter.changeLoadStatus(NewsDetailAdapter.NO_MORE_DATA); } } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition(); } });
在RecyclerView滑動的時候獲取lastVisibleItemPosition,在RecyclerView的滑動狀態處於空閒且已經滑動到最底部的時候,做加載數據的操作,同時在此時可以限制RecyclerView的加載條目數,在上面的onScrollStateChanged方法中,我調用Adapte的getItemCount方法獲取現在RecyclerView裡的條目數,如果其大於等於360條的話,則不做加載更多數據,只提示沒有更多數據了。 上面已經實現了上拉加載功能,為了讓界面更友好一點,我們可以給RecyclerView添加一個FooterView,用於提示用戶做一些操作。 首先准備一個FootView布局:
android:id="@+id/tv_footer" android:layout_width="match_parent" android:layout_height="90dp" android:gravity="center" android:padding="10dp" android:text="上拉刷新..." android:textColor="@android:color/darker_gray" android:textSize="18sp" />一個TextView,用於顯示一些文字,提示用戶可以做的操作或應用正在做的操作。 修改Adapter的getItemViewType方法:
@Override public int getItemViewType(int position) { if (headerView == null) { NewsModel currNM = mList.get(position - 1); if (currNM.imgextra == null || currNM.imgextra.size() == 0) return NORMAL; return MULTIIMAGE; } if (position == 0) return HEADER; if (position + 1 == getItemCount()) return FOOTER; NewsModel currNM = mList.get(position - 1); if (currNM.imgextra == null || currNM.imgextra.size() == 0) return NORMAL; return MULTIIMAGE; }
在Adapter中增加一個ViewHolder:
public class ViewHolderFooter extends RecyclerView.ViewHolder { private TextView text; public ViewHolderFooter(View itemView) { super(itemView); text = (TextView) itemView; } }
修改Adapter的onCreateViewHolder方法:
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (headerView != null && viewType == HEADER) return new ViewHolderNormal(headerView); if (viewType == NORMAL) { View view = LayoutInflater.from(mContext).inflate(R.layout.item_news_detail_rv, parent, false); return new ViewHolderNormal(view); } if (viewType == FOOTER) { TextView view = (TextView) LayoutInflater.from(mContext).inflate(R.layout.layout_footer, parent, false); return new ViewHolderFooter(view); } View view = LayoutInflater.from(mContext).inflate(R.layout.item_image_news_detail_rv, parent, false); return new ViewHolderMultiImage(view); }
修改Adapter的onBindViewHolder方法:
@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) { if (getItemViewType(position) == HEADER) return; if (getItemViewType(position) == FOOTER) { ViewHolderFooter footerHolder = (ViewHolderFooter) holder; switch (load_status) { case LOAD_MORE: footerHolder.text.setText("上拉加載更多..."); break; case LOADING: footerHolder.text.setText("正在加載更多數據..."); break; case NO_MORE_DATA: footerHolder.text.setText("沒有更多數據"); break; } return; } final int pos = getRealPosition(holder); NewsModel currNM = mList.get(pos); switch (getItemViewType(position)) { case NORMAL: ViewHolderNormal normalHolder = (ViewHolderNormal) holder; normalHolder.tv_title.setText(currNM.title); normalHolder.tv_author.setText(currNM.source); normalHolder.tv_zan.setText(currNM.replyCount + "跟帖"); Glide.with(mContext).load(currNM.imgsrc).into(normalHolder.iv_item_news_detail_rv); break; case MULTIIMAGE: ViewHolderMultiImage imageHolder = (ViewHolderMultiImage) holder; imageHolder.tv_title.setText(currNM.title); imageHolder.tv_author.setText(currNM.source); imageHolder.tv_zan.setText(currNM.replyCount + "跟帖"); Glide.with(mContext).load(currNM.imgsrc).into(imageHolder.iv_item_news_detail_rv); if (currNM.imgextra != null && currNM.imgextra.size() > 1) { Glide.with(mContext).load(currNM.imgextra.get(0).imgsrc).into(imageHolder.iv_center); Glide.with(mContext).load(currNM.imgextra.get(1).imgsrc).into(imageHolder.iv_right); } break; } }
看到上面的方法中可以根據不同的狀態更改FooterView的文字內容,可以在Adapter中定義一個public方法,用於改變status:
// 改變Footer布局的文字 public void changeLoadStatus(int status) { load_status = status; notifyDataSetChanged(); }
這樣,在上拉加載的時候,用戶就可以看到對應的提示。正如開頭的圖片中展示的那樣。 完整源碼參看:https://github.com/miyuexingchen/Swen/blob/master/app/src/main/java/com/wcc/swen/adapter/NewsDetailAdapter.java
Android自定義DataTimePicker(日期選擇器) 筆者有一段時間沒有發表關於Android的文章了,關於Android自定義組件筆者有好幾篇想跟大家分享的
一、前言前一篇文章已經詳細介紹了如何使用Xposed框架編寫第一個微信插件:搖骰子和猜拳作弊器 本文繼續來介紹如何使用Xposed框架編寫第二個微信插件,可以將本地小視頻
先上圖,看看接下來我要向大家介紹的是個什麼東西,如下圖: public View findTargetView(float x, float y, View anc
概述在上文,酷炫Path動畫已經預告了,今天給大家帶來的是利用 純自定義View,實現的仿餓了麼加入購物車控件,自帶閃轉騰挪動畫的按鈕。 效果圖如下:圖1 項目中使用的效