Android TV橫向滾動網格佈局——RecyclerView的使用
最近在做一個Android盒子的專案,主要是Launcher有一個橫向滾動的介面。主要使用的是RecyclerView。總結一下。
一、先了解下RecyclerView
RecyclerView是類似於ListView、GridView的一種AdapterView。相比較的優勢是使用更加靈活,可以滿足實現更多不同的效果。
在我要實現的水平滾動網格佈局中就得到了很好的滿足。因為使用HorizentalScrollView + GridView的模式會十分複雜,並且焦點、動作的監聽會比較混亂。事件衝突處理起來特別麻煩。
二、實現簡單例子
記錄一下我自己學習的過程。先寫了一下RecyclerViewTest的工程。這個工程的主要效果是在頁面上顯示Android裝置上所安裝的所有應用。並且點選應用圖示可以進入相應的應用。點選選單鍵可以解除安裝該應用。
大概就是這樣:
1.先要關聯recyclerview的jar包:
dependencies {
...
compile 'com.android.support:recyclerview-v7:23.0.1'
}
2.然後就可以使用RecyclerView了,先根據需求自定義了一個SimpleRecyclerView.java
public class SimpleRecycleView extends RecyclerView {
private static final String TAG = SimpleRecycleView.class.getSimpleName();
// 一個滾動物件
private Scroller mScroller;
private int mLastX = 0;
public SimpleRecycleView(Context context) {
super(context);
init(context);
}
public SimpleRecycleView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SimpleRecycleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
// 一個初始化方法,傳入了一個上下文物件,用來初始化滾動物件
private void init(Context context){
mScroller = new Scroller(context);
}
// 重寫了計算滾動方法
@Override
public void computeScroll() {
if(mScroller!=null && mScroller.computeScrollOffset()){
scrollBy(mLastX - mScroller.getCurrX(), 0);
mLastX = mScroller.getCurrX();
postInvalidate();
}
}
/**
* 呼叫此方法滾動到目標位置,其中(fx, fy)表示最終要滾到的目標位置的座標值
* duration表示期間滾動的耗時。
*
* @param fx 目標位置的X向座標值
* @param fy 目標位置的Y向座標值
* @param duration 滾動到目標位置所消耗的時間毫秒值
*/
@SuppressWarnings("unused")
public void smoothScrollTo(int fx, int fy,int duration) {
int dx = 0;
int dy = 0;
// 計算變化的位移量
if(fx != 0) {
dx = fx - mScroller.getFinalX();
}
if(fy!=0) {
dy = fy - mScroller.getFinalY();
}
Log.i(TAG, "fx:" + fx + ", getFinalX:" + mScroller.getFinalX() + ", dx:" + dx);
smoothScrollBy(dx, dy, duration);
}
/**
* 呼叫此方法設定滾動的相對偏移
*/
public void smoothScrollBy(int dx, int dy, int duration) {
if(duration > 0) {
mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy, duration);
} else {
// 設定mScroller的滾動偏移量
mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);
}
// 重繪整個view,重繪過程會呼叫到computeScroll()方法。
// 這裡必須呼叫invalidate()才能保證computeScroll()會被呼叫,否則不一定會重新整理介面,看不到滾動效果
invalidate();
}
/**
* 此方法用來檢查自動調節
*
* @param position 要檢查的位置
*/
@SuppressWarnings("unused")
public void checkAutoAdjust(int position){
int childCount = getChildCount();
// 獲取可視範圍內的選項的頭尾位置
int firstVisibleItemPosition = ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
int lastVisibleItemPosition = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();
Log.d(TAG, "childCount:" + childCount + ", position:" + position + ", firstVisibleItemPosition:" + firstVisibleItemPosition
+ " lastVisibleItemPosition:" + lastVisibleItemPosition);
if(position == (firstVisibleItemPosition + 1) || position == firstVisibleItemPosition){
// 當前位置需要向右平移
leftScrollBy(position, firstVisibleItemPosition);
} else if (position == (lastVisibleItemPosition - 1) || position == lastVisibleItemPosition){
// 當前位置需要向左平移
rightScrollBy(position, lastVisibleItemPosition);
}
}
private void leftScrollBy(int position, int firstVisibleItemPosition){
View leftChild = getChildAt(0);
if(leftChild != null){
int startLeft = leftChild.getLeft();
int endLeft = (position == firstVisibleItemPosition ? leftChild.getWidth() : 0);
Log.d(TAG, "startLeft:" + startLeft + " endLeft" + endLeft);
autoAdjustScroll(startLeft, endLeft);
}
}
private void rightScrollBy(int position, int lastVisibleItemPosition){
int childCount = getChildCount();
View rightChild = getChildAt(childCount - 1);
if(rightChild != null){
int startRight = rightChild.getRight() - getWidth();
int endRight = (position == lastVisibleItemPosition ? (-1 * rightChild.getWidth()) : 0);
Log.d(TAG,"startRight:" + startRight + " endRight:" + endRight);
autoAdjustScroll(startRight, endRight);
}
}
/**
*
* @param start 滑動起始位置
* @param end 滑動結束位置
*/
private void autoAdjustScroll(int start, int end){
mLastX = start;
mScroller.startScroll(start, 0, end - start, 0);
postInvalidate();
}
/**
* 將指定item平滑移動到整個view的中間位置
* @param position 指定的item的位置
*/
public void smoothScrollMaster(int position) {
// 這個方法是為了設定Scroller的滾動的,需要根據業務需求,編寫演算法。
}
}
3.然後就可以在佈局檔案中使用自定義的控制元件了:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/home_background">
<com.jiuzhou.porter.launcher.widget.SimpleRecycleView
android:id="@+id/home_apps"
android:layout_marginLeft="@dimen/px_positive_80"
android:layout_marginRight="@dimen/px_positive_80"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scrollbars="none" />
</RelativeLayout>
我去不小心暴露了我的包名(@^_^@)
4.在MainActivity.java中編寫程式碼:
//初始化RecyclerView,設定佈局管理器、間距、介面卡、資料等
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main)
...
// 1.初始化SimpleRecyclerView
mRecyclerView = (SimpleRecycleView) findViewById(R.id.home_apps);
// 2.使用自定義的工具類獲得裝置中安裝的APP
mListOfApps = LauncherCommonUtils.getAllApk(this);
// 3.初始化介面卡
SimpleRecyclerAdapter mAdapter = new SimpleRecyclerAdapter(this, mListOfApps);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
// 4.設定佈局管理器:瀑布流式
StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(3 , StaggeredGridLayoutManager.HORIZONTAL);
// 5.根據需要設定間距等其他內容
mRecyclerView.setLayoutManager(staggeredGridLayoutManager);
int right = (int) getResources().getDimension(R.dimen.px_positive_5);
int bottom = (int) getResources().getDimension(R.dimen.px_positive_1);
RecyclerView.ItemDecoration spacingInPixel = new SpaceItemDecoration(right, bottom);
mRecyclerView.addItemDecoration(spacingInPixel);
// 6.關聯介面卡
mRecyclerView.setAdapter(mAdapter);
}
- 這裡有必要說一下:
關於佈局管理器的內容:
RecyclerView的使用時是必須設定佈局管理器的。因為不同的佈局管理器決定了展現出來的演示是怎樣的。
常見的集中佈局管理器有LinearLayoutManager、RelativeLayoutManager、GridLayoutManager、StaggeredGridLayoutManager等。
意思一目瞭然。只提一下StaggeredGridLayoutManager,這個是水平方向的Grid
這裡比較重要的是Adapter
5. 關於SimpleRecyclerAdapter
/*
* Copyright (c) 2016. Project Launcher
* Source SimpleRecyclerAdapter
* Author 沈煜
* 此原始碼及相關文件等附件由 沈煜 編寫,作者保留所有權利
* 使用必須註明出處。
* The code and documents is write by the author. All rights are reserved.
* Use must indicate the source.
*
*/
public class SimpleRecyclerAdapter extends RecyclerView.Adapter<SimpleRecyclerAdapter.ViewHolder>{
private static final String TAG = SimpleRecyclerAdapter.class.getSimpleName();
private LayoutInflater mInflater;
private List<AppBean> mListOfApps;
private int currentPosition = 0;
private Context context;
public SimpleRecyclerAdapter(Context context, List<AppBean> mListOfApps){
mInflater = LayoutInflater.from(context);
this.context = context;
this.mListOfApps = mListOfApps;
}
@SuppressWarnings("unused")
public void setData(List<AppBean> mListOfApps){
this.mListOfApps = mListOfApps;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.item_grid_apps, parent, false);
ViewHolder vh = new ViewHolder(view);
vh.mImageView = (ImageView) view.findViewById(R.id.home_grid_item_icon);
vh.mTextView = (TextView) view.findViewById(R.id.home_grid_item_name);
return vh;
}
private View mOldFocus;
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
holder.mImageView.setImageDrawable(mListOfApps.get(position).getAppIcon());
holder.mTextView.setText(mListOfApps.get(position).getAppName());
// 設定itemView可以獲得焦點
holder.itemView.setFocusable(true);
holder.itemView.setTag(position);
holder.itemView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
currentPosition = (int) holder.itemView.getTag();
mOnItemSelectListener.onItemSelect(holder.itemView, currentPosition);
if (v != mOldFocus) {
View vb = v.findViewById(R.id.home_back_2);
GradientDrawable gd = (GradientDrawable) vb.getBackground();
int width = (int) context.getResources().getDimension(R.dimen.px_positive_3);
int color = context.getResources().getColor(R.color.color0);
int radius = (int) context.getResources().getDimension(R.dimen.px_positive_25);
gd.setStroke(width, color);
gd.setCornerRadius(radius);
if (mOldFocus != null) {
View ovb = mOldFocus.findViewById(R.id.home_back_2);
GradientDrawable ogd = (GradientDrawable) ovb.getBackground();
ogd.setStroke(0, Color.parseColor("#00000000"));
}
}
mOldFocus = v;
} else {
if (v != null) {
View ovb2 = v.findViewById(R.id.home_back_2);
GradientDrawable ogd2 = (GradientDrawable) ovb2.getBackground();
ogd2.setStroke(0, Color.parseColor("#00000000"));
}
}
}
});
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnItemClickListener.onItemClick(v, currentPosition);
}
});
holder.itemView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
mOnItemKeyListener.OnItemKey(v, keyCode, event, currentPosition);
return false;
}
});
}
@Override
public int getItemCount() {
return mListOfApps.size();
}
private int index = 0;
class ViewHolder extends RecyclerView.ViewHolder{
ImageView mImageView;
TextView mTextView;
ViewHolder(View itemView) {
super(itemView);
ImageView back2 = (ImageView) itemView.findViewById(R.id.home_back_2);
GradientDrawable background = (GradientDrawable) back2.getBackground();
TypedArray ta = context.getResources().obtainTypedArray(R.array.appBackgroundColors);
int count = ta.length();
int [] colorsArray = new int[count];
for (int i=0;i<count;i++) {
int resId = ta.getResourceId(i, -1);
colorsArray[i] = resId;
}
/*Random random = new Random();
int index = random.nextInt(count);
while (oldIndex == index) {
index = random.nextInt();
}
oldIndex = index;*/
background.setColor(context.getResources().getColor(colorsArray[index]));
if (index < count - 1) {
index += 1;
} else {
index = 0;
}
ta.recycle();
}
}
private OnItemSelectListener mOnItemSelectListener;
private OnItemClickListener mOnItemClickListener;
private OnItemLongClickListener mOnItemLongClickListener;
private OnItemKeyListener mOnItemKeyListener;
public interface OnItemSelectListener {
void onItemSelect(View view, int position);
}
public interface OnItemClickListener {
void onItemClick(View view, int position);
}
public interface OnItemLongClickListener {
void onItemLongClick(View view, int position);
}
public interface OnItemKeyListener {
void OnItemKey(View view, int keyCode, KeyEvent event, int position);
}
public void setOnItemSelectListener(OnItemSelectListener listener){
mOnItemSelectListener = listener;
}
public void setOnItemClickListener(OnItemClickListener mOnItemClickListener) {
this.mOnItemClickListener = mOnItemClickListener;
}
public void setOnItemLongClickListener(OnItemLongClickListener mOnItemLongClickListener) {
this.mOnItemLongClickListener = mOnItemLongClickListener;
}
public void setOnItemKeyListener(OnItemKeyListener mOnItemKeyListener) {
this.mOnItemKeyListener = mOnItemKeyListener;
}
}
最後有一個重要的地方就是Adapter中寫了許多監聽器。這個是RecyclerView特有的,因為RecyclerView沒有監聽器!!!(簡直了,不能忍好嘛。所以要在Adapter中自己定義監聽。因為你監聽的是其中的itemView,當然了也可以去RecyclerView裡面寫諸如OnItemClick這樣的監聽器,會麻煩一點。不過那樣封裝起來比較牛。去GitHub上應該有這樣的jar可以用。)
差不多了吧
————————-不怎麼華麗的分割線—————–
MDZZ,這個裡面的焦點控制忘了寫!!!厲害了我的哥,下期詳解。
相關推薦
Android TV橫向滾動網格佈局——RecyclerView的使用
最近在做一個Android盒子的專案,主要是Launcher有一個橫向滾動的介面。主要使用的是RecyclerView。總結一下。 一、先了解下RecyclerView RecyclerView是類似於ListView、GridView的一種Adap
Android--(9)--詳解網格佈局(GridLayout)
GridLayout佈局特點:將整個螢幕分成行*列的形式,每個網格上放一個元件; 在往網格中放置控制元件的時候, 會自動按照水平或垂直方向新增。 幾個常用屬性 android:rowCou
Android TV端的(RecyclerView)水平滾動焦點錯亂問題
boolean uestc spa cas roi enabled ati pix eve package com.hhzt.iptv.ui.customview;import android.content.Context;import android.content.r
Android RecyclerView網格佈局的學習
最近用到 RecyclerView的網格佈局,簡單學習了一個Demo,效果如下 下面是程式碼 public class GridViewDemo extends AppCompatActivity { private RecyclerView recyclerView
Android RecyclerView +SnapHelper 實現橫向滾動自動滾動到中心控制元件並選中
效果圖 此效果已被產品砍掉,所以有些適配bug就不修改了 此部落格只為記錄下程式碼 默哀3秒 1秒 2秒 3秒 程式碼 佈局檔案 <?xml version="1.0" encoding="utf-8"?> <
Android網格佈局實現--recyclerview
接上一篇內容,使用RecyclerView實現;<android.support.v7.widget.RecyclerView android:id="@+id/rvGrid" android:layout_width="match_parent" a
Android為TV端助力 listview與recyclerview上下聯動
pre get reat ins -s tlist ping cto desc 首先是主布局fragment裏面的xml文件 <?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:andro
Android Studio 相對佈局/網格佈局
1. 相對佈局(RelativeLayout) 1.1 相對佈局視窗內子元件的位置總是相對兄弟元件、父容器來決定的,因此叫相對佈局 1.2 如果A元件位置是由B元件的位置決定的,Android要求先定B元件,再定義A元件 如果A元件位置是由
Android的網格佈局
網格佈局GridLayout 1.常用屬性rowCount(行數)、columnCount(列數) GridLayout中子控制元件相關屬性:layout_gravity="fill_horizontal(水平填充)|fill_vertical(垂直填充)" &
Android佈局之網格佈局
1. 什麼是佈局 就是把介面中的控制元件按照某種規律擺放到指定的位置 2. 佈局的二種實現 程式碼 xml配置檔案:res/layout目錄下 注:也可以同時使用xml和程式
Android相對佈局和網格佈局
案例一:相對佈局 相對佈局(重點) 1.1 相對佈局視窗內子元件的位置總是相對兄弟元件、父容器來決定的,因此叫相對佈局 1.2 如果A元件位置是由B元件的位置決定的,Android要求先定B元件,再定義A元件 注1:注意XML中元件的順序,不然會報錯 注2:
android的相對佈局和網格佈局
案例一:相對佈局 相對佈局(重點) 1.1 相對佈局視窗內子元件的位置總是相對兄弟元件、父容器來決定的,因此叫相對佈局 1.2 如果A元件位置是由B元件的位置決定的,Android要求先定B元件,再定義A元件 如果A元件位置是由B元件的位置決定的,Android要求先定
flex佈局下el-table橫向滾動條失效
如下圖,是一種常見的頁面結構,我們可以有很多方法實現,inline-block,float,flex等等 但是,最近專案中遇到一個怪事,左邊是側邊欄導航,右邊是一個數據展示table,el-table的橫向滾動條死活不出來。 我是採用flex佈局,這裡簡單貼一下css原始碼 : (page 頁面根容器 s
Android 應用開發(50)---GridLayout(網格佈局)
GridLayout(網格佈局) 今天要介紹的佈局是Android 4.0以後引入的一個新的佈局,和前面所學的TableLayout(表格佈局) 有點類似,不過他有很多前者沒有的東西,也更加好用, 可以自己設定佈局中元件的排列方式 可以自定義網格佈局有多少行,多少
Android佈局2(相對佈局和網格佈局)
1. 相對佈局(RelativeLayout 重點:) 1.1 相對佈局視窗內子元件的位置總是相對兄弟元件、父容器來決定的(就是根據旁邊的足跡來設定位置),因此叫相對佈局 1.2 如果A元件位置是由B元件的位置決定的,Android要求先定B元件,再定義A元件
Android-仿千度尺的橫向滾動選擇器
先上一個效果圖 主要核心方法,這裡有一個問題ontouchEvent如果返回super,則move事件不會繼續執行,down事件則沒問題.所以這裡要返回true 其次,就是計算滾動距離的問題 @Override public boolean onTouchEvent(Mot
Android 實現一個簡易橫向流式佈局
SimpleFlowLayout:一個簡易的橫向流式佈局,只實現核心功能,使用者可自行擴充套件 Demo圖片如下所示: SimpleFlowLayout直接繼承自ViewGroup,主要負責
Android 小樣之TextView橫向滾動(跑馬燈效果)
偶爾做app的時候由於文字過多,但是又不想換行顯示,影響整體佈局效果,可以使用文字橫向滾動效果。 使用繼承Android原生TextView控制元件實現 設定xml檔案TextView屬性
Android之ScrollView滾動佈局控制元件使用以及顯示新聞網頁
ScrollView滾動佈局使用原理: ①滾動產生的條件是,裡面的內容大於物理尺寸 ②ScrollView裡面只有一個子元素,這個子元素就是一個線性佈局LinearLayout,我們可以線上性佈局中新增我們需要的內容,所以ScrollView中得包裹一層,並且線性佈局中設計
RecyclerView實現標題-網格,標題-網格佈局
公司讓實現標題--網格,標題--網格的佈局,想來想去就用Recyclerview來實現了,說實話,中間休息了一段時間,recyclerview我用的並不多,為了工作,只能向前。。。後來在網上也是各種扒資料,最終實現了效果,雖然效果實現了,但是裡面還有些地方是自己不太明白的,特