編輯:關於Android編程
在寫博客園客戶端的時候,突然想到,弄個知乎日報風格的簡單清爽多好!不需要那麼多繁雜的信息干擾視野。先貼上效果圖,左邊是知乎日報的,右邊是本方案的
本文所使用的ide是androidStudio
首先我們需要在項目中,引入RecyclerView、CardView
在build.gradle的 dependencies 添加兩條引用語句,如
dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') compile 'com.android.support:appcompat-v7:22.0.0' compile 'com.android.support:cardview-v7:22.0.0' compile 'com.android.support:recyclerview-v7:22.0.0' compile 'com.android.support:support-v4:22.0.0' }
先來簡單說下這兩個控件
support-v7包中的新組件,是一個強大的滑動組件,與經典的ListView相比,同樣擁有item回收復用的功能,但是直接把viewholder的實現封裝起來,用戶只要實現自己的viewholder就可以了,該組件會自動幫你回收復用每一個item。它不但變得更精簡,也變得更加容易使用,而且更容易組合設計出自己需要的滑動布局。
我們先來看下,在調試裡面,開啟布局邊界選項,知乎日報是什麼樣子的
看到日期了沒,如果某條新聞比前面新聞要早一天,頭上就會出現日期。類似於按照日期分組的功能。
那麼在ListView裡,我們要實現這個功能,可能每個新聞裡面都要添加個文本控件,如果早一天,則顯示該控件。缺點不多說了,很丑陋
RecyclerView的強大之處在於,他將新聞要引用哪個布局的主動權,交給了新聞本身(而不是RecyclerView)。
也就是說,我可以有兩個布局文件,
新聞帶日期的
新聞不帶日期的
具體使用哪個布局,子類自己決定。
這個控件倒沒什麼特別要說的,布局類似FrameLayout,但是加了很多特效:卡片的邊框、陰影,duang,很酷、很炫。。
一點題外話:RecyclerView我之前接觸了好幾次,很想學,但是和ListView比起來,在很多功能上會有不同(這都是更先進的做法,吸取了ListView很多不足,不然谷歌也不會推出這個包),因此,如果你覺得熟練使用ListView就可以了,不需要了解RecyclerView的話,請相信我,花費的時間絕對值得!你可能會覺得使用ListView+CardView也能實現知乎日報的布局,但是拋開性能不說,CardView在ListView裡面是沒法使用布局屬性的!也就是說layout_margin是無效的。你的卡片就只能擠在一起了!
好,介紹完了,咱們就開始探索,如何實現知乎日報的風格吧!
裡面包了一個線性布局,左邊文字,右邊圖片
我們使用include引入第二步不帶標題的CardView(復用),在此基礎上,添加了個TextView顯示日期
下面我們開始逐步介紹關鍵的後台綁定類NewsListAdapter.java
ListView有一堆各式各樣的Adapter可以綁定。那麼對應RecyclerView,它的Adpter很簡單,注意泛型參數必須繼承自ViewHolder。
public class NewsListAdapter extends RecyclerView.Adapter{
什麼是ViewHolder,官方是這樣解釋的:
對於RecyclerView裡面的某個元素,ViewHolder持有了該元素的布局和數據信息。我們實現ViewHolder時,最好可以添加一些屬性,來緩存一些需要花費資源處理的結果。
是不是很熟悉?ListView裡面,我們也有ViewHolder的,那套網上廣泛流傳的提高ListView性能的法則裡面,就有一個使用自定義ViewHolder來緩存元素的布局信息以提高性能。
不記得啦?看看下面的代碼吧
RecyclerView的強大之處就在於,它本身就提供了ViewHolder,我們只要繼承自該ViewHolder就可以了,至於ViewHolder怎麼存儲,系統會自動幫我們搞定。
OK,解釋清楚了什麼是ViewHolder,接下來是咱自己實現的新聞、帶標題的新聞ViewHolder
/** * 新聞標題 */ public class NormalItemHolder extends RecyclerView.ViewHolder { TextView newsTitle; ImageView newsIcon; public NormalItemHolder(View itemView) { super(itemView); newsTitle = (TextView) itemView.findViewById(R.id.base_swipe_item_title); newsIcon = (ImageView) itemView.findViewById(R.id.base_swipe_item_icon); itemView.findViewById(R.id.base_swipe_item_container).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showNewsDetail(getPosition()); } }); } } /** * 帶日期新聞標題 */ public class GroupItemHolder extends NormalItemHolder { TextView newsTime; public GroupItemHolder(View itemView) { super(itemView); newsTime = (TextView) itemView.findViewById(R.id.base_swipe_group_item_time); } }
我們重寫父類RecyclerView.Adapter的兩個方法就好了。
1:生成一個標志
/** * 決定元素的布局使用哪種類型 * @param position 數據源List的下標 * @return 一個int型標志,傳遞給onCreateViewHolder的第二個參數 */ @Override public int getItemViewType(int position) {
2:根據第一步的標志調用對應的ViewHolder
/** * 渲染具體的ViewHolder * @param viewGroup ViewHolder的容器 * @param i 一個標志,我們根據該標志可以實現渲染不同類型的ViewHolder * @return */ @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
重寫父類的綁定方法就好了。
/** * 綁定ViewHolder的數據。 * @param viewHolder * @param i 數據源list的下標 */ @Override public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i)
上面所述關鍵的三個父類方法,重寫後就可以達到我們要求了,下面貼上完整的代碼,構造方法有兩個參數:activity,數據源List
供各位參考
package zhexian.app.zoschina.news; import android.support.v7.widget.RecyclerView; import android.text.Html; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import java.util.List; import zhexian.app.zoschina.R; import zhexian.app.zoschina.base.BaseActionBarActivity; import zhexian.app.zoschina.lib.ZImage; import zhexian.app.zoschina.util.ConfigConstant; public class NewsListAdapter extends RecyclerView.Adapter{ private static final int NORMAL_ITEM = 0; private static final int GROUP_ITEM = 1; private BaseActionBarActivity mContext; private List mDataList; private LayoutInflater mLayoutInflater; public NewsListAdapter(BaseActionBarActivity mContext, List mDataList) { this.mContext = mContext; this.mDataList = mDataList; mLayoutInflater = LayoutInflater.from(mContext); } /** * 渲染具體的ViewHolder * @param viewGroup ViewHolder的容器 * @param i 一個標志,我們根據該標志可以實現渲染不同類型的ViewHolder * @return */ @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { if (i == NORMAL_ITEM) { return new NormalItemHolder(mLayoutInflater.inflate(R.layout.fragment_base_swipe_item, viewGroup, false)); } else { return new GroupItemHolder(mLayoutInflater.inflate(R.layout.fragment_base_swipe_group_item, viewGroup, false)); } } /** * 綁定ViewHolder的數據。 * @param viewHolder * @param i 數據源list的下標 */ @Override public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) { NewsListEntity entity = mDataList.get(i); if (null == entity) return; if (viewHolder instanceof GroupItemHolder) { bindGroupItem(entity, (GroupItemHolder) viewHolder); } else { NormalItemHolder holder = (NormalItemHolder) viewHolder; bindNormalItem(entity, holder.newsTitle, holder.newsIcon); } } @Override public int getItemCount() { return mDataList.size(); } /** * 決定元素的布局使用哪種類型 * @param position 數據源List的下標 * @return 一個int型標志,傳遞給onCreateViewHolder的第二個參數 */ @Override public int getItemViewType(int position) { //第一個要顯示時間 if (position == 0) return GROUP_ITEM; String currentDate = mDataList.get(position).getPublishDate(); int prevIndex = position - 1; boolean isDifferent = !mDataList.get(prevIndex).getPublishDate().equals(currentDate); return isDifferent ? GROUP_ITEM : NORMAL_ITEM; } @Override public long getItemId(int position) { return mDataList.get(position).getNewsID(); } void bindNormalItem(NewsListEntity entity, TextView newsTitle, ImageView newsIcon) { if (entity.getIconUrl().isEmpty()) { if (newsIcon.getVisibility() != View.GONE) newsIcon.setVisibility(View.GONE); } else { ZImage.getInstance().load(entity.getIconUrl(), newsIcon, ConfigConstant.LIST_ITEM_IMAGE_SIZE_DP, ConfigConstant.LIST_ITEM_IMAGE_SIZE_DP, true, mContext.getMyApplication().canRequestImage()); if (newsIcon.getVisibility() != View.VISIBLE) newsIcon.setVisibility(View.VISIBLE); } newsTitle.setText(Html.fromHtml(entity.getTitle())); } void bindGroupItem(NewsListEntity entity, GroupItemHolder holder) { bindNormalItem(entity, holder.newsTitle, holder.newsIcon); holder.newsTime.setText(entity.getPublishDate()); } void showNewsDetail(int pos) { NewsListEntity entity = mDataList.get(pos); NewsDetailActivity.actionStart(mContext, entity.getNewsID(), entity.getRecommendAmount(), entity.getCommentAmount()); } /** * 新聞標題 */ public class NormalItemHolder extends RecyclerView.ViewHolder { TextView newsTitle; ImageView newsIcon; public NormalItemHolder(View itemView) { super(itemView); newsTitle = (TextView) itemView.findViewById(R.id.base_swipe_item_title); newsIcon = (ImageView) itemView.findViewById(R.id.base_swipe_item_icon); itemView.findViewById(R.id.base_swipe_item_container).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showNewsDetail(getPosition()); } }); } } /** * 帶日期新聞標題 */ public class GroupItemHolder extends NormalItemHolder { TextView newsTime; public GroupItemHolder(View itemView) { super(itemView); newsTime = (TextView) itemView.findViewById(R.id.base_swipe_group_item_time); } } }
ps,咱在代碼裡面,將日期格式統一轉換成友好格式(今日、昨日、6月6日星期6),在數據綁定的時候,和前面一條對比,如果不一樣,則使用帶日期的格式。就這樣。
奉上日期友好格式生成代碼
public static int daysOfTwo(Date originalDate, Date compareDateDate) { Calendar aCalendar = Calendar.getInstance(); aCalendar.setTime(originalDate); int originalDay = aCalendar.get(Calendar.DAY_OF_YEAR); aCalendar.setTime(compareDateDate); int compareDay = aCalendar.get(Calendar.DAY_OF_YEAR); return originalDay - compareDay; } public static String FriendlyDate(Date compareDate) { Date nowDate = new Date(); int dayDiff = daysOfTwo(nowDate, compareDate); if (dayDiff <= 0) return "今日"; else if (dayDiff == 1) return "昨日"; else if (dayDiff == 2) return "前日"; else return new SimpleDateFormat("M月d日 E").format(compareDate); }
status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream, int index, aud
Android動畫 實現開關按鈕動畫(屬性動畫之平移動畫),最近做項目,根據項目需求,有一個這樣的功能,實現類似開關的動畫效果,經過自己琢磨及上網查找資料,終於解決了,這
先來上個效果圖:當滑動時:數值顯示,滑動停止時顯示數字,使用FrameLayout結合SeekBar。首先我們看看。Layout:<?xml version
還是先看看效果圖,免得浪費大家的時間 1.第三方框架有很多,這裡采用的是MPAndroidChart,github鏈接下