Android5.x:RecycleView(一):實現ListView + GridView + StaggeredGridLayou效果
1 RecycleView實現ListView的功能
需要新增依賴:
compile 'com.android.support:recyclerview-v7:24.2.0'
相關方法:
RecyclerView的方法:
方法 | 含義 |
---|---|
setLayoutManager(…) | 設定佈局管理者 |
setAdapter | 設定介面卡 |
Adapter中的方法:
方法 | 含義 |
---|---|
onCreateViewHolder(…) | 建立ViewHolder |
onBindViewHolder(…) | 給ViewHolder裡面的view設定屬性 |
getItemCount | item的數量 |
步驟
- 找到RecycleView
- 給RecycleView設定 LayoutManager
- 給RecycleView設定 Adapter
下面通過一個demo顯示RecycleView的基本使用。
見圖:
MainActivity
package com.cqc.recyclerview01;
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private Context context;
private List<String> list = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = this;
initData();
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false));
recyclerView.setAdapter(new MyAdapter());
}
private void initData() {
list.clear();
for (int i = 0; i < 100; i++) {
list.add("item" + i);
}
}
public class MyAdapter extends RecyclerView.Adapter {
private MyHolder myHolder;
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
TextView tv = new TextView(context);
tv.setHeight(50);
myHolder = new MyHolder(tv);
return myHolder;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
// MyHolder myHolder = (MyHolder) holder;
myHolder = (MyHolder) holder;
myHolder.textView.setText(list.get(position));
}
@Override
public int getItemCount() {
return 100;
}
}
public class MyHolder extends RecyclerView.ViewHolder {
public TextView textView;
public MyHolder(View itemView) {
super(itemView);
textView = (TextView) itemView;
}
}
}
實現item的分割線
com.android.support:recyclerview-v7:25.0.1
官方有了預設的DividerItemDecoration
,25.0.1之前沒有這個類。
我們發現recyclerview沒有分割線,需要呼叫mRecyclerView.addItemDecoration()新增分割線,
但是ItemDecoration是抽象類,需要我們自己實現。
怎麼新增預設的分割線?
匯入類DividerItemDecoration到專案中,呼叫方法:
recyclerView.addItemDecoration(new DividerItemDecoration(context,LinearLayoutManager.VERTICAL));
//或者
recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL));
注意:注意context為null的情況,儘量用getActivity()
或者getContext()
,最近就報了錯,找了半天才發現是這裡錯了。儘量不要讓context
變成成員變數,
private Context context = getActivity();
怎麼新增自定義分割線 ?
建立自定義的分割線:item_divider.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:height="4dp"/><!--不用設定寬度-->
<gradient
android:centerColor="#ff0000ff"
android:endColor="#ffff0000"
android:startColor="#ff00ff00"
android:type="linear"/>
</shape>
<!--recyclerView分割線的設定-->
使用自定義的分割線:styles.xml
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
...
<!--自定義item的分割線-->
<item name="android:listDivider">@drawable/item_divider</item>
</style>
</resources>
效果圖:
package com.cqc.recyclerview01;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
/**
* RecyclerView新增分割線(當使用LayoutManager為LinearLayoutManager時)
* 該類源於:http://blog.csdn.net/lmj623565791/article/details/45059587
*/
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private Drawable mDivider;
private int mOrientation;
public DividerItemDecoration(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
setOrientation(orientation);
}
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
@Override
public void onDraw(Canvas c, RecyclerView parent) {
Log.v("recyclerview - itemdecoration", "onDraw()");
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
public void drawVertical(Canvas c, RecyclerView parent) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext());
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
}
然後在mainActivity中呼叫:
recyclerView.addItemDecoration(new DividerItemDecoration(context,LinearLayoutManager.VERTICAL));//新增分割線
新增點選事件
方法一:在ViewHolder的構造方法裡面寫點選監聽事件
static class MyViewHolder extends RecyclerView.ViewHolder {
TextView tv;
public MyViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int adapterPosition = getAdapterPosition();
}
});
}
}
方法二:在onBindViewHolder(...)
裡面寫itemView的點選監聽
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, final int position) {
MyViewHolder holder = (MyViewHolder) viewHolder;
holder.tv.setText("" + position);
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "position=" + position);
}
});
}
方法三:利用回撥,在Activity中寫點選監聽
先在adapter中設定回撥
//設定點選回撥
public interface OnItemClickListener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
private OnItemClickListener mOnItemClickListener;
public void setOnItemClickListener(OnItemClickListener mOnItemClickListener) {
this.mOnItemClickListener = mOnItemClickListener;
}
然後在onBindViewHolder(…)中呼叫點選事件監聽
//如果設定了回撥,則呼叫點選事件
if (mOnItemClickListener != null) {
holder.textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnItemClickListener.onItemClick(v,position);
}
});
holder.textView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
mOnItemClickListener.onItemLongClick(v, position);
return true;//事件被處理
}
});
}
如果只有短點選,同理也需要在onBindViewHolder()中設定.
//如果設定了回撥,則呼叫點選事件
if (mOnItemClickListener != null) {
holder.textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnItemClickListener.onItemClick(v,position);
}
});
}
public interface OnItemClickListener {
void onItemClick(View view, int position);
}
private OnItemClickListener mOnItemClickListener;
public void setOnItemClickListener(OnItemClickListener mOnItemClickListener) {
this.mOnItemClickListener = mOnItemClickListener;
}
Mactivity中呼叫
adapter.setOnItemClickListener(new MyAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
}
});
最後在activity中使用點選事件
//點選事件(用adapter呼叫點選方法)
adapter.setOnItemClickListener(new MyAdapter.OnItemClickListener() {
@Override
public void onItemClickListerner(View view, int position) {
Toast.makeText(context,"短點選",Toast.LENGTH_SHORT).show();
}
@Override
public void onItemLongClickListener(View view, int position) {
Toast.makeText(context,"長點選",Toast.LENGTH_SHORT).show();
}
});
效果圖:
新增item和刪除item
方法:
新增item:adapter.notifyItemInserted(position)
刪除item:adapter.notifyItemRemoved(position);
btn_add.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
list.add(0,"btn_add");
// adapter.notifyDataSetChanged();//也可以,但是沒有動畫效果
adapter.notifyItemInserted(0);
recyclerView.scrollToPosition(0);//滑動到第一個item,不加不會滑動到頂部。
}
});
btn_delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
list.remove(0);
adapter.notifyDataSetChanged();//也可以,但是沒有動畫效果
// adapter.notifyItemRemoved(0);
}
});
item新增/刪除的動畫
recyclerView.setItemAnimator(new DefaultItemAnimator());//item新增移出動畫
notifyItemRemoved(int position)引起的角標越界異常
在呼叫該方法後,
list.remove(position);
adapter.notifyItemRemoved(position);
容易引起角標越界異常,因為呼叫該方法後,不會重新走onBindViewHolder(…),所以position的值沒有變化。
解決方法:
list.remove(position);
adapter.notifyItemRemoved(position);
if (position != list.size()) {//如果刪除的是最後一個,則不重新整理資料。
adapter.notifyItemRangeChanged(position, list.size() - deleteIndex);//重新整理position後的資料
}
新增HeadView和FootView
1 這裡只討論新增一個HeadView或FootView
2 下面的是沒有給HeadView或FootView建立單獨的ViewHolder,用的是同一個ViewHolder,並在其構造方法中判斷;
3 當然也可以給HeadView或FootView建立單獨的ViewHolder, 並在onCreateViewHolder(…)中返回對應的ViewHolder。
首先定義HeadView和FootView的型別
//預設只加一個header 和footer,2種類型不能為0,因為getItemViewType(int position)預設返回0
private int HeaderType = 1;
private int FooterType = 2;
其次:定義HeadView和FootView變數,並設定和獲取他們的數量(0/1)
private View headerView;
private View footerView;
public void setHeaderView(View headerView) {
this.headerView = headerView;
}
public void setFooterView(View footerView) {
this.footerView = footerView;
}
public int getHeadViewCount() {
return headerView == null ? 0 : 1;
}
public int getFootViewCount() {
return footerView == null ? 0 : 1;
}
第三步:重寫方法getItemViewType(int position)
此處注意:定義HeaderType 和FooterType的值不能是0,因為該方法預設返回0,如果頭或尾定義了0,而你又沒有給其它item(HeadView/FootView之外的item)定義type,仍然使用
return super.getItemViewType(position);
,那麼0就代表2種類型了,這是不對的。
@Override
public int getItemViewType(int position) {
if (position == 0) {
return HeaderType;
}
if (position == 100 + getHeadViewCount()) {
return FooterType;
}
return super.getItemViewType(position);
}
上面的是確定有header,其實不應該這樣判斷
@Override
public int getItemViewType(int position) {
if (position < getHeadViewCount()) {
return HEAD;
}
if (position >= list.size() + getHeadViewCount()) {
return FOOT;
}
return NORMAL;
}
第四步:修改Adapter中的3個方法
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == HeaderType) {
return new MyViewHolder(headerView);
}
if (viewType == FooterType) {
return new MyViewHolder(footerView);
}
View itemView = View.inflate(context, android.R.layout.two_line_list_item, null);
return new MyViewHolder(itemView);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
if (getItemViewType(position) == HeaderType) {
return;
}
if (getItemViewType(position) == FooterType) {
return;
}
MyViewHolder holder = (MyViewHolder) viewHolder;
holder.tv1.setText("Title");
holder.tv2.setText("message");
}
@Override
public int getItemCount() {
return 100 + getHeadViewCount() + getFootViewCount();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView tv_item;
public MyViewHolder(View itemView) {
super(itemView);
if (itemView == headerView) {
return;
}
if (itemView == footerView) {
return;
}
tv_item = (TextView) itemView.findViewById(R.id.tv_item);
}
}
第五步:我們新增HeadView和FootView,setHeaderView(view)放在setAdapter(adapter)前後無所謂。
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(context));
adapter = new MyAdapter();
recyclerView.setAdapter(adapter);
View headView = View.inflate(context, R.layout.layout_header, null);
View footView = View.inflate(context, R.layout.layout_footer, null);
adapter.setHeaderView(headView);
adapter.setFooterView(footView);
這是我們發現:HeadView或FootView的寬度沒有充滿螢幕,這是什麼原因呢? 詳見:Android基礎:三種inflate的區別
這是因為我們在填充HeadView或FootView使用的是
View headView = View.inflate(context, R.layout.layout_header, null);
View footView = View.inflate(context, R.layout.layout_footer, null);
改成下面這種,注意:第二個引數parent是recyclerView
View headView = LayoutInflater.from(context).inflate(R.layout.layout_header,recyclerView, false);
View footView =LayoutInflater.from(context).inflate(R.layout.layout_footer, recyclerView,false);
setHasFixedSize(true)
recyclerView.setHasFixedSize(true);
它的作用是當我們隊資料進行增刪的時候,不會重新計算item寬高。
注意事項
1 onCreateViewHolder(…)不可以複用holder,必須new。
if (myHolder == null){
myHolder = new MyHolder(tv);
}
2 onCreateViewHolder(…)中的holder和onBindViewHolder(RecyclerView.ViewHolder holder, int position) {。。}中的holder就是我們建立的MyHolder,可以直接進行格式轉換,也可以宣告成員變數。
public class MyAdapter extends RecyclerView.Adapter {
private MyHolder myHolder;
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
TextView tv = new TextView(context);
tv.setHeight(50);
myHolder = new MyHolder(tv);
return myHolder;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
// MyHolder myHolder = (MyHolder) holder;
myHolder = (MyHolder) holder;
myHolder.textView.setText(list.get(position));
}
@Override
public int getItemCount() {
return 100;
}
}
3 最好先建立類ViewHolder,再建立Adapter,這樣可以使用直接指定泛型,就不需要再格式轉換了。
public class MyAdapter extends RecyclerView.Adapter<MyHolder>
4 item的高度設為match_parent,父佈局設定100dp,item的高無效
父佈局設定具體的高度,子view設match_parent,這是無效的。
方法一:必須給子view(textview設定具體的數值),父佈局math——parent
方法二:item的view:使用 LayoutInflater.from(mContext).inflate(R.layout.item_layout, parent, false); 而不是其他任何方法!(View.inflate(..))
5 item的子view(textview)寬度match_parent,父佈局的寬度也是match_parent,但是仍然無法充滿螢幕。
方法:item的view:使用LayoutInflater.from(mContext).inflate(R.layout.item_layout, parent, false); 而不是其他任何方法!(View.inflate(..))
6 總結:結合4+5,使用View.inflate(context, R.layout.item_main, null);建立item的view,會導致item的view高度無效,子view(textview)的高寬無效。
方法:item的view:使用LayoutInflater.from(mContext).inflate(R.layout.item_layout, parent, false); 而不是其他任何方法!
錯誤:(View.inflate(context, R.layout.item_main, null)
錯誤:LayoutInflater.from(context).inflate(R.layout.item_main,null))
7 item寬度沒有充滿螢幕
RecyclerView子View寬度不能全屏的問題,在Adapter的onCreateViewHolder建立子view的時候要把parent傳進去;
//這種inflate方式有事會導致item寬度不充滿螢幕
//View itemView = View.inflate(parent.getContext(), R.layout.item_gate_frag, null);
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_gate_frag,parent,false);
複雜的RecyclerView
和listview一樣,複雜的RecyclerView即使item有多種型別。這是需要重寫adapter的2方法
,定義View型別:
getItemViewType(int position) {...} getItemCount() {...}private static int ViewTypeHead = 0; private static int ViewTypeBody = 1;
,在建立ViewHolder是也需要判斷ViewType
,有幾種型別就建立幾個ViewHolder
。
以下是主要程式碼:
private static int ViewTypeHead = 0;
private static int ViewTypeBody = 1;
@Override
public int getItemViewType(int position) {
if (position % 3 == 0) {
return ViewTypeHead;
} else {
return ViewTypeBody;
}
}
@Override
public int getItemCount() {
return 100;
}
public class ItemHeadViewHeader extends RecyclerView.ViewHolder {
...
public ItemHeadViewHeader(View itemView) {
super(itemView);
...
}
}
public class ItemBodyViewHeader extends RecyclerView.ViewHolder {
...
public ItemBodyViewHeader(View itemView) {
super(itemView);
...
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == ViewTypeHead) {
View itemView = View.inflate(parent.getContext(), R.layout.item_header, null);
return new ItemHeadViewHeader(itemView);
} else {
View itemView = View.inflate(parent.getContext(), R.layout.item_body, null);
return new ItemBodyViewHeader(itemView);
}
}
RecycleView(二):實現GridView的功能
同ListView基本一樣,只是LayoutManager變成了GridLayoutManager
效果圖
程式碼
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new GridLayoutManager(MainActivity.this, 3));
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.addItemDecoration(new DividerGridItemDecoration(MainActivity.this));
adapter = new RecyclerGridAdapter(list);
recyclerView.setAdapter(adapter);
adapter.setOnItemClickListener(new RecyclerGridAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
toast.setText(list.get(position));
toast.show();
}
});
分割線
RecyclerView實現GridView沒有提供預設的分割線(25.0.1開始有了),V7包中的DividerItemDecoration
只是給ListView
使用,而GridView
應該使用自定義的DividerGridItemDecoration
DividerGridItemDecoration的下載地址
效果圖:
自定義分割線
ListView的divider 只需要設定高度
GridView的divider 需要設定高度+寬度
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<gradient
android:centerColor="#ff00ff00"
android:endColor="#ff0000ff"
android:startColor="#ffff0000"
android:type="linear" />
<!--listView的divider 只需要設定高度-->
<!--gridView 的divider 需要設定高度+寬度-->
<size android:height="4dp" android:width="4dp"/>
</shape>
item的margin無效的問題
margin無效 效果圖: margin有效 效果圖
要想使itemView 跟佈局的layout_margin生效,必須指定root即parent,也就是不能使用第一種方式建立itemView
//第一種
View itemView = View.inflate(parent.getContext(), R.layout.item_grid, null);
//第二種
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_grid, parent, false);
RecylerView實現瀑布流
效果圖
同ListView和GridView的不同之處
recyclerView.setLayoutManager(new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL));
recyclerView.addItemDecoration(new DividerGridItemDecoration(StaggeredGridActivity.this));
怎麼是這種效果的?
通過設定每個item的隨機高度,達到顯示瀑布流的效果。
heights.add(random.nextInt(100) + 100);
ViewGroup.LayoutParams params = holder.tv.getLayoutParams();
params.height = heights.get(position);
holder.tv.setLayoutParams(params);
ScrollView巢狀RecylerView
下面的內容來源自android ScrollView巢狀RecyclerView ,之驗證了重寫LinearLayoutManager
,可以正常使用,而且乜有重寫ScrollView
。
ScrollView
巢狀ListView
,我們一般重寫onMeasure()
方法,但是RecyclerView
不行,而是要重寫LayoutManager
,比如LinearLayoutManager
、GridLayoutManager
和StaggeredGridLayoutManager
。但是值得注意的是,如果recyclerView
很長那麼強烈不建議去做巢狀,因為這樣recyclerView
會在展示的時候立刻展示所有內容,效率極低。
重寫LinearLayoutManager
public class FullyLinearLayoutManager extends LinearLayoutManager {
private static final String TAG = FullyLinearLayoutManager.class.getSimpleName();
public FullyLinearLayoutManager(Context context) {
super(context);
}
public FullyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
public FullyLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
private int[] mMeasuredDimension = new int[2];
@Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
int widthSpec, int heightSpec) {
final int widthMode = View.MeasureSpec.getMode(widthSpec);
final int heightMode = View.MeasureSpec.getMode(heightSpec);
final int widthSize = View.MeasureSpec.getSize(widthSpec);
final int heightSize = View.MeasureSpec.getSize(heightSpec);
Log.i(TAG, "onMeasure called. \nwidthMode " + widthMode
+ " \nheightMode " + heightSpec
+ " \nwidthSize " + widthSize
+ " \nheightSize " + heightSize
+ " \ngetItemCount() " + getItemCount());
int width = 0;
int height = 0;
for (int i = 0; i < getItemCount(); i++) {
measureScrapChild(recycler, i,
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
mMeasuredDimension);
if (getOrientation() == HORIZONTAL) {
width = width + mMeasuredDimension[0];
if (i == 0) {
height = mMeasuredDimension[1];
}
} else {
height = height + mMeasuredDimension[1];
if (i == 0) {
width = mMeasuredDimension[0];
}
}
}
switch (widthMode) {
case View.MeasureSpec.EXACTLY:
width = widthSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
switch (heightMode) {
case View.MeasureSpec.EXACTLY:
height = heightSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
setMeasuredDimension(width, height);
}
private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
int heightSpec, int[] measuredDimension) {
try {
View view = recycler.getViewForPosition(0);//fix 動態新增時報IndexOutOfBoundsException
if (view != null) {
RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
getPaddingLeft() + getPaddingRight(), p.width);
int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
getPaddingTop() + getPaddingBottom(), p.height);
view.measure(childWidthSpec, childHeightSpec);
measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
recycler.recycleView(view);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
}
重寫GridLayoutManager
public class FullyGridLayoutManager extends GridLayoutManager {
public FullyGridLayoutManager(Context context, int spanCount) {
super(context, spanCount);
}
public FullyGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
super(context, spanCount, orientation, reverseLayout);
}
private int[] mMeasuredDimension = new int[2];
@Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
final int widthMode = View.MeasureSpec.getMode(widthSpec);
final int heightMode = View.MeasureSpec.getMode(heightSpec);
final int widthSize = View.MeasureSpec.getSize(widthSpec);
相關推薦
Android5.x:RecycleView(一):實現ListView + GridView + StaggeredGridLayou效果
1 RecycleView實現ListView的功能
需要新增依賴:
compile 'com.android.support:recyclerview-v7:24.2.0'
相關方法:
RecyclerView的方法:
IO流的應用(一):實現檔案的複製
package com.bjpowernode.demo03;
import java.io.FileReader; import java.io.FileWriter; import java.io.IOException;
/**
使用FileReader/FileW
安卓專案實戰之強大的網路請求框架okGo使用詳解(一):實現get,post基本網路請求,下載上傳進度監聽以及對Callback自定義的深入理解
1.新增依賴
//必須使用
compile 'com.lzy.net:okgo:3.0.4'
//以下三個選擇新增,okrx和okrx2不能同時使用,一般選擇新增最新的rx2支援即可
compile 'com.lzy.net:okrx:1.0.2'
compile 'com.lzy
關於wxpy的小實驗(一):實現登入微信、訊息接收、處理、回覆和人臉檢測處理反饋
概述:本文主要是博主想分享一下最近在學習python和opencv時做的一些小實驗和作為自己程式設計之路剛開始的一個小筆記。在剛接觸python時發現了有一個叫wxpy的東西,他可以實現讓微信自動接收、
Android二維碼掃描開發(一):實現思路與原理
【 回覆“ 1024 ”,送你一個特別推送 】
現在二維碼已經非常普及了,那麼二維碼的掃描與處理也成為了Android開發中的一個必要技能。網上有很多關於Android中二維碼處理的帖子,大都是在講開源框架zxing用法,然後貼貼程式碼就完了,並沒有一個系統的分析和
Mina、Netty、Twisted一起學(一):實現簡單的TCP伺服器
MINA、Netty、Twisted為什麼放在一起學習?首先,不妨先分別看一下它們官方網站對其的介紹:MINA:Apache MINA is a network application framework which helps users develop high perf
SignalR 2.x入門(一):SignalR簡單例子
本系列教程使用工具
開發工具:VS2015
.NET版本:4.5
SignalR 版本:2.x系列
建立空Asp.Net Web專案,在程式包管理器控制檯中輸入如下命令,安裝SignalR:
install-package Microsoft.AspNet.Signa
分散式服務架構學習(一):實現自己的RPC框架(採用Java Socket)
RPC實現原理圖:
1、Service API對應服務介面。
HelloService.java程式碼如下:
package com.shan.rpc.service;
public interface HelloService {
public String
VST SDK 3.x 開發(一):VST結構介紹
注:所有文章內容均可在VST SDK的doc資料夾中找到英文版。如果你英文非常好可以忽略這個系列的文章直接看原版。本文並不是翻譯而是個人對說明文件的理解
一、VST音訊外掛
VST(Virtual Sound Technology)音訊外掛是Steinberg公司創造的。簡
分布式鎖實現大型連續劇之(一):Redis
set 但是 sss channel 時有 commands 阻塞 iss cond 前言:
單機環境下我們可以通過JAVA的Synchronized和Lock來實現進程內部的鎖,但是隨著分布式應用和集群環境的出現,系統資源的競爭從單進程多線程的競爭變成了多進程的競爭,這時
前端案例分享(一):CSS+JS實現流星雨動畫
目錄
引言
1、效果圖
2、原始碼
3、案例解析
4、小問題
5、結語
引言
平常會做一些有意思的小案例練手,通常都會發到codepen上,但是codepen不能寫分析。 &nb
Java併發(四):volatile的實現原理 Java併發(一):Java記憶體模型乾貨總結
synchronized是一個重量級的鎖,volatile通常被比喻成輕量級的synchronized
volatile是一個變數修飾符,只能用來修飾變數。
volatile寫:當寫一個volatile變數時,JMM會把該執行緒對應的本地記憶體中的共享變數重新整理到主記憶體。
volatile讀:當讀一
Caffe視覺化(一):網路結構視覺化(用Caffe自帶程式實現)
Caffe視覺化(一):網路結構視覺化(用Caffe自帶程式實現)
本文記錄瞭如何利用Caffe自帶的程式實現網路的視覺化,包括可能遇到的問題和解決方案。更新於2018.10.25。
文章目錄
Caffe視覺化(一):網路結構視覺化(用Caffe自帶程式實現
資料結構實現(一):動態陣列(C++版)
資料結構實現(一):動態陣列(C++版)
1. 概念及基本框架
2. 基本操作程式實現
2.1 增加操作
2.2 刪除操作
2.3 修改操作
2.4 查詢操作
2.5 其他操作
3. 演算法複雜度分析
Spring Boot Actuator詳解與深入應用(一):Actuator 1.x
《Spring Boot Actuator詳解與深入應用》預計包括三篇,第一篇重點講Spring Boot Actuator 1.x的應用與定製端點;第二篇將會對比Spring Boot Actuator 2.x 與1.x的區別,以及應用和定製2.x的端點;第三篇將會介紹Actuator metric指
JAVA高階基礎(8)---Set的典型實現(一):HashSet
HHashSet
注:更多詳細方法請自行在 API 上查詢
HashSet 是由hash表(hashMap)支援,不保證元素的迭代順恆久不變,允許存在null值,元素不允許重複,同時,不是執行緒安全的
HashSet是基於HashMap實現的。
&n
用python來實現機器學習(一):線性迴歸(linear regression)
需要下載一個data:auto-mpg.data
第一步:顯示資料集圖
import pandas as pd
import matplotlib.pyplot as plt
columns = ["mpg","cylinders","displacement","horsepowe
Asp.net Core 使用Jenkins + Dockor 實現持續整合、自動化部署(一):Jenkins安裝
寫在前面
其實園子裡很多大佬都寫過,我也是一個搬運工很多東西不是原創的,不過還是想把自己安裝的過程,記錄下來如果能幫到大家的忙,也是一件功德無量的事;
執行環境
centos:7.2 cpu:1核 2G記憶體 1M頻寬 其實用的騰訊雲
安裝jenkins
這裡的jenkins就不從docker
java多執行緒系列(一):Thread、Runnable、Callable實現多執行緒的區別
實現多執行緒
java實現多執行緒的方法有三種,分別是繼承thread類,實現runnable介面,實現callable介面(call方法有返回值)
/**
* 繼承Thread
*/
public class MyThread extends Thread{
int a = 0;
JAVA 學習(一):16進位制字串自增的實現
JAVA學習系列,並不是從基礎去講java的知識,而是把我在學習或是工作中,一些思想、邏輯總結出來。
原先在工作中,因為測試的需要,經常要往資料庫中批量的插資料。而表的主鍵用的是UUID,是由16進位制字元加“-”組成的,還有裝置的mac地址是由16進位制字元加“:”組成的,那個時候,我剛學ja