1.解决首页按方向键光标移动错误定位问题
This commit is contained in:
@@ -65,6 +65,7 @@ import com.ik.mboxlauncher.view.MultiView;
|
|||||||
import com.ik.mboxlauncher.view.CustomRecyclerView;
|
import com.ik.mboxlauncher.view.CustomRecyclerView;
|
||||||
import com.ik.mboxlauncher.view.SplashView;
|
import com.ik.mboxlauncher.view.SplashView;
|
||||||
import com.ik.mboxlauncher.view.TimeTextView;
|
import com.ik.mboxlauncher.view.TimeTextView;
|
||||||
|
import com.ik.mboxlauncher.view.TvRecyclerView;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -79,7 +80,8 @@ public class Launcher extends FragmentActivity implements SplashView.SplashAdLi
|
|||||||
private MultiView layout_video,layout_music,layout_primevideo,layout_filemanager,layout_hbomax,layout_setting,layout_youtube,layout_recommend,layout_netflix,layout_chrome;
|
private MultiView layout_video,layout_music,layout_primevideo,layout_filemanager,layout_hbomax,layout_setting,layout_youtube,layout_recommend,layout_netflix,layout_chrome;
|
||||||
private MultiView layout_miracastreceive;
|
private MultiView layout_miracastreceive;
|
||||||
private int[] imageResIds={R.drawable.img_video,R.drawable.img_miracastreceive,R.drawable.img_youtube,R.drawable.img_recommend,R.drawable.img_music,R.drawable.img_netflix,R.drawable.img_primevideo,R.drawable.img_filemanager,R.drawable.img_chrome,R.drawable.img_hbomax,R.drawable.img_setting};
|
private int[] imageResIds={R.drawable.img_video,R.drawable.img_miracastreceive,R.drawable.img_youtube,R.drawable.img_recommend,R.drawable.img_music,R.drawable.img_netflix,R.drawable.img_primevideo,R.drawable.img_filemanager,R.drawable.img_chrome,R.drawable.img_hbomax,R.drawable.img_setting};
|
||||||
private CustomRecyclerView gv_shortcut,grid_coustom_apps;
|
private CustomRecyclerView gv_shortcut;
|
||||||
|
private TvRecyclerView grid_coustom_apps;
|
||||||
private StatusLoader mStatusLoader;
|
private StatusLoader mStatusLoader;
|
||||||
private ShortAppInfoAdapter mShortAppInfoAdapter=null;
|
private ShortAppInfoAdapter mShortAppInfoAdapter=null;
|
||||||
private CustomAppAdapter mCustomAppAdapter=null;
|
private CustomAppAdapter mCustomAppAdapter=null;
|
||||||
@@ -878,7 +880,7 @@ public boolean onGenericMotionEvent(MotionEvent event) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
translateAnimation.setDuration(300);
|
translateAnimation.setDuration(360);
|
||||||
translateAnimation.setFillAfter(true);
|
translateAnimation.setFillAfter(true);
|
||||||
content_view.startAnimation(translateAnimation);
|
content_view.startAnimation(translateAnimation);
|
||||||
|
|
||||||
@@ -895,7 +897,7 @@ public boolean onGenericMotionEvent(MotionEvent event) {
|
|||||||
cuttentModel = MODEL_NORMAL;
|
cuttentModel = MODEL_NORMAL;
|
||||||
LogUtils.loge("coustom_view.getHeight():"+coustom_view.getLayoutParams().height);
|
LogUtils.loge("coustom_view.getHeight():"+coustom_view.getLayoutParams().height);
|
||||||
TranslateAnimation translateAnimation = new TranslateAnimation(0.0f, 0.0f,(float)(0 - coustom_view.getLayoutParams().height - gv_shortcut.getHeight()),0.0f);
|
TranslateAnimation translateAnimation = new TranslateAnimation(0.0f, 0.0f,(float)(0 - coustom_view.getLayoutParams().height - gv_shortcut.getHeight()),0.0f);
|
||||||
translateAnimation.setDuration(300);
|
translateAnimation.setDuration(360);
|
||||||
translateAnimation.setAnimationListener(new Animation.AnimationListener() {
|
translateAnimation.setAnimationListener(new Animation.AnimationListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationStart(Animation animation) {
|
public void onAnimationStart(Animation animation) {
|
||||||
@@ -906,6 +908,7 @@ public boolean onGenericMotionEvent(MotionEvent event) {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationEnd(Animation animation) {
|
public void onAnimationEnd(Animation animation) {
|
||||||
|
LogUtils.loge("dismissCustomApp onAnimationEnd()");
|
||||||
coustom_view.setVisibility(View.GONE);
|
coustom_view.setVisibility(View.GONE);
|
||||||
gv_shortcut.requestFocus();
|
gv_shortcut.requestFocus();
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ public class CustomAppAdapter extends RecyclerView.Adapter<CustomAppAdapter.View
|
|||||||
if(hasFocus){
|
if(hasFocus){
|
||||||
viewHolder.item_name.setSelected(true);
|
viewHolder.item_name.setSelected(true);
|
||||||
viewHolder.itemView.setBackgroundResource(R.drawable.app_item_select);
|
viewHolder.itemView.setBackgroundResource(R.drawable.app_item_select);
|
||||||
viewHolder.itemView.requestFocus();
|
// viewHolder.itemView.requestFocus();
|
||||||
scaleView(viewHolder.itemView, 1.07f);
|
scaleView(viewHolder.itemView, 1.07f);
|
||||||
// viewHolder.itemView.post(() -> viewHolder.itemView.requestFocus());
|
// viewHolder.itemView.post(() -> viewHolder.itemView.requestFocus());
|
||||||
}else {
|
}else {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.ik.mboxlauncher.ui.fragment;
|
|||||||
|
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewTreeObserver;
|
||||||
import android.view.animation.TranslateAnimation;
|
import android.view.animation.TranslateAnimation;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -25,6 +26,7 @@ import com.ik.mboxlauncher.ui.adapter.VideoAppAdapter;
|
|||||||
import com.ik.mboxlauncher.ui.adapter.MyAppInfoAdapter;
|
import com.ik.mboxlauncher.ui.adapter.MyAppInfoAdapter;
|
||||||
import com.ik.mboxlauncher.ui.base.BaseFragment;
|
import com.ik.mboxlauncher.ui.base.BaseFragment;
|
||||||
import com.ik.mboxlauncher.view.CustomRecyclerViewer;
|
import com.ik.mboxlauncher.view.CustomRecyclerViewer;
|
||||||
|
import com.ik.mboxlauncher.view.TvRecyclerView;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -34,7 +36,7 @@ public abstract class CategoryFragment extends BaseFragment implements AppnetCal
|
|||||||
|
|
||||||
private CategoryAppPresenter mCategoryAppPresenter =null;
|
private CategoryAppPresenter mCategoryAppPresenter =null;
|
||||||
// private MyAppInfoAdapter mMyAppInfoAdapter = null;
|
// private MyAppInfoAdapter mMyAppInfoAdapter = null;
|
||||||
protected RecyclerView grid_coustom_apps;
|
protected TvRecyclerView grid_coustom_apps;
|
||||||
protected CustomRecyclerViewer gv_category_apps;
|
protected CustomRecyclerViewer gv_category_apps;
|
||||||
protected ImageView img_logo,img_tab_video, img_tab_recommend, img_tab_apps, img_tab_music, img_tab_local;
|
protected ImageView img_logo,img_tab_video, img_tab_recommend, img_tab_apps, img_tab_music, img_tab_local;
|
||||||
protected TextView tv_title;
|
protected TextView tv_title;
|
||||||
@@ -288,7 +290,21 @@ public abstract class CategoryFragment extends BaseFragment implements AppnetCal
|
|||||||
|
|
||||||
LogUtils.loge("data size is "+shortcutInfoBeanList.size());
|
LogUtils.loge("data size is "+shortcutInfoBeanList.size());
|
||||||
mCustomAppAdapter.addDatas(shortcutInfoBeanList);
|
mCustomAppAdapter.addDatas(shortcutInfoBeanList);
|
||||||
|
grid_coustom_apps.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||||
|
@Override
|
||||||
|
public void onGlobalLayout() {
|
||||||
|
grid_coustom_apps.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||||
|
grid_coustom_apps.scrollToPosition(0);
|
||||||
|
grid_coustom_apps.post(()->{
|
||||||
|
int firstVisibleItemPosition = ((GridLayoutManager) grid_coustom_apps.getLayoutManager()).findFirstVisibleItemPosition();
|
||||||
|
RecyclerView.ViewHolder holderview = grid_coustom_apps.findViewHolderForAdapterPosition(firstVisibleItemPosition);
|
||||||
|
if(holderview !=null){
|
||||||
|
View targetView = holderview.itemView;
|
||||||
|
targetView.post(() -> targetView.requestFocus());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}else {
|
}else {
|
||||||
LogUtils.loge("no data");
|
LogUtils.loge("no data");
|
||||||
}
|
}
|
||||||
|
|||||||
686
app/src/main/java/com/ik/mboxlauncher/view/TvRecyclerView.java
Normal file
686
app/src/main/java/com/ik/mboxlauncher/view/TvRecyclerView.java
Normal file
@@ -0,0 +1,686 @@
|
|||||||
|
package com.ik.mboxlauncher.view;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.FocusFinder;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
|
||||||
|
|
||||||
|
import com.ik.mboxlauncher.R;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ksl on 2026/1/9.
|
||||||
|
*/
|
||||||
|
public class TvRecyclerView extends RecyclerView {
|
||||||
|
private static final String TAG = "TvRecyclerView";
|
||||||
|
|
||||||
|
private int position;
|
||||||
|
private int lastFocusPosition;
|
||||||
|
//焦点是否居中
|
||||||
|
private boolean mSelectedItemCentered;
|
||||||
|
|
||||||
|
private boolean hasResetFocus;//自控焦点
|
||||||
|
|
||||||
|
private boolean mScrollDirecitonUp;//默认向下滚
|
||||||
|
|
||||||
|
private int mSelectedItemOffsetStart;
|
||||||
|
|
||||||
|
private int mSelectedItemOffsetEnd;
|
||||||
|
|
||||||
|
//分页的时候使用
|
||||||
|
private int mLoadMoreBeforehandCount = 0;
|
||||||
|
private PressBackListenner mPressbackLister;
|
||||||
|
|
||||||
|
public TvRecyclerView(Context context) {
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TvRecyclerView(Context context, AttributeSet attrs) {
|
||||||
|
this(context, attrs, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TvRecyclerView(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
init(context, attrs, defStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
initView();
|
||||||
|
initAttr(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initView() {
|
||||||
|
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
|
||||||
|
setHasFixedSize(true);
|
||||||
|
setWillNotDraw(true);
|
||||||
|
setOverScrollMode(View.OVER_SCROLL_NEVER);
|
||||||
|
setChildrenDrawingOrderEnabled(true);
|
||||||
|
|
||||||
|
setClipChildren(false);
|
||||||
|
setClipToPadding(false);
|
||||||
|
|
||||||
|
setClickable(false);
|
||||||
|
setFocusable(true);
|
||||||
|
setFocusableInTouchMode(true);
|
||||||
|
/**
|
||||||
|
防止RecyclerView刷新时焦点不错乱bug的步骤如下:
|
||||||
|
(1)adapter执行setHasStableIds(true)方法
|
||||||
|
(2)重写getItemId()方法,让每个view都有各自的id
|
||||||
|
(3)RecyclerView的动画必须去掉
|
||||||
|
*/
|
||||||
|
setItemAnimator(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initAttr(Context context, AttributeSet attrs) {
|
||||||
|
if (attrs != null) {
|
||||||
|
final TypedArray a = context.obtainStyledAttributes(attrs,
|
||||||
|
R.styleable.TvRecyclerView);
|
||||||
|
/**
|
||||||
|
* 如果是towWayView的layoutManager
|
||||||
|
*/
|
||||||
|
final String name = a.getString(R.styleable.TvRecyclerView_tv_layoutManager);
|
||||||
|
|
||||||
|
|
||||||
|
mSelectedItemCentered = a.getBoolean(R.styleable.TvRecyclerView_tv_selectedItemCentered, false);
|
||||||
|
|
||||||
|
mLoadMoreBeforehandCount = a.getInteger(R.styleable.TvRecyclerView_tv_loadMoreBeforehandCount, 0);
|
||||||
|
|
||||||
|
mSelectedItemOffsetStart = a.getDimensionPixelSize(R.styleable.TvRecyclerView_tv_selectedItemOffsetStart, 0);
|
||||||
|
|
||||||
|
mSelectedItemOffsetEnd = a.getDimensionPixelSize(R.styleable.TvRecyclerView_tv_selectedItemOffsetEnd, 0);
|
||||||
|
|
||||||
|
a.recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int getFreeWidth() {
|
||||||
|
return getWidth() - getPaddingLeft() - getPaddingRight();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getFreeHeight() {
|
||||||
|
return getHeight() - getPaddingTop() - getPaddingBottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
|
||||||
|
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
|
||||||
|
// View lastFocusedView = getLayoutManager().findViewByPosition(lastFocusPosition);
|
||||||
|
// if(lastFocusedView!=null){
|
||||||
|
// lastFocusedView.requestFocus();
|
||||||
|
// }
|
||||||
|
if(!gainFocus){
|
||||||
|
hasResetFocus=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasFocus() {
|
||||||
|
return super.hasFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInTouchMode() {
|
||||||
|
// 解决4.4版本抢焦点的问题
|
||||||
|
if (Build.VERSION.SDK_INT == 19) {
|
||||||
|
return !(hasFocus() && !super.isInTouchMode());
|
||||||
|
} else {
|
||||||
|
return super.isInTouchMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
//return super.isInTouchMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void requestChildFocus(View child, View focused) {
|
||||||
|
|
||||||
|
if (null != child) {
|
||||||
|
if (mSelectedItemCentered) {
|
||||||
|
mSelectedItemOffsetStart = !isVertical() ? (getFreeWidth() - child.getWidth()) : (getFreeHeight() - child.getHeight());
|
||||||
|
mSelectedItemOffsetStart /= 2;
|
||||||
|
mSelectedItemOffsetEnd = mSelectedItemOffsetStart;
|
||||||
|
}
|
||||||
|
lastFocusPosition=getChildViewHolder(child).getAdapterPosition();
|
||||||
|
Log.e(TAG,"lastFocusPosition="+lastFocusPosition);
|
||||||
|
}
|
||||||
|
super.requestChildFocus(child, focused);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
|
||||||
|
final int parentLeft = getPaddingLeft();
|
||||||
|
final int parentRight = getWidth() - getPaddingRight();
|
||||||
|
|
||||||
|
final int parentTop = getPaddingTop();
|
||||||
|
final int parentBottom = getHeight() - getPaddingBottom();
|
||||||
|
|
||||||
|
final int childLeft = child.getLeft() + rect.left;
|
||||||
|
final int childTop = child.getTop() + rect.top;
|
||||||
|
|
||||||
|
final int childRight = childLeft + rect.width();
|
||||||
|
final int childBottom = childTop + rect.height();
|
||||||
|
|
||||||
|
final int offScreenLeft = Math.min(0, childLeft - parentLeft - mSelectedItemOffsetStart);
|
||||||
|
final int offScreenRight = Math.max(0, childRight - parentRight + mSelectedItemOffsetEnd);
|
||||||
|
|
||||||
|
final int offScreenTop = Math.min(0, childTop - parentTop - mSelectedItemOffsetStart);
|
||||||
|
final int offScreenBottom = Math.max(0, childBottom - parentBottom + mSelectedItemOffsetEnd);
|
||||||
|
|
||||||
|
|
||||||
|
final boolean canScrollHorizontal = getLayoutManager().canScrollHorizontally();
|
||||||
|
final boolean canScrollVertical = getLayoutManager().canScrollVertically();
|
||||||
|
|
||||||
|
// Favor the "start" layout direction over the end when bringing one side or the other
|
||||||
|
// of a large rect into view. If we decide to bring in end because start is already
|
||||||
|
// visible, limit the scroll such that start won't go out of bounds.
|
||||||
|
final int dx;
|
||||||
|
if (canScrollHorizontal) {
|
||||||
|
if (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL) {
|
||||||
|
dx = offScreenRight != 0 ? offScreenRight
|
||||||
|
: Math.max(offScreenLeft, childRight - parentRight);
|
||||||
|
} else {
|
||||||
|
dx = offScreenLeft != 0 ? offScreenLeft
|
||||||
|
: Math.min(childLeft - parentLeft, offScreenRight);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Favor bringing the top into view over the bottom. If top is already visible and
|
||||||
|
// we should scroll to make bottom visible, make sure top does not go out of bounds.
|
||||||
|
final int dy;
|
||||||
|
if (canScrollVertical) {
|
||||||
|
dy = offScreenTop != 0 ? offScreenTop : Math.min(childTop - parentTop, offScreenBottom);
|
||||||
|
} else {
|
||||||
|
dy = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (dx != 0 || dy != 0) {
|
||||||
|
if (immediate) {
|
||||||
|
scrollBy(dx, dy);
|
||||||
|
} else {
|
||||||
|
smoothScrollBy(dx, dy);
|
||||||
|
}
|
||||||
|
// 重绘是为了选中item置顶,具体请参考getChildDrawingOrder方法
|
||||||
|
postInvalidate();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBaseline() {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int getSelectedItemOffsetStart() {
|
||||||
|
return mSelectedItemOffsetStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSelectedItemOffsetEnd() {
|
||||||
|
return mSelectedItemOffsetEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLayoutManager(LayoutManager layout) {
|
||||||
|
super.setLayoutManager(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是垂直,还是横向.
|
||||||
|
*/
|
||||||
|
private boolean isVertical() {
|
||||||
|
LayoutManager manager = getLayoutManager();
|
||||||
|
if (manager != null) {
|
||||||
|
LinearLayoutManager layout = (LinearLayoutManager) getLayoutManager();
|
||||||
|
return layout.getOrientation() == LinearLayoutManager.VERTICAL;
|
||||||
|
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置选中的Item距离开始或结束的偏移量;
|
||||||
|
* 与滚动方向有关;
|
||||||
|
* 与setSelectedItemAtCentered()方法二选一
|
||||||
|
*
|
||||||
|
* @param offsetStart
|
||||||
|
* @param offsetEnd 从结尾到你移动的位置.
|
||||||
|
*/
|
||||||
|
public void setSelectedItemOffset(int offsetStart, int offsetEnd) {
|
||||||
|
setSelectedItemAtCentered(false);
|
||||||
|
mSelectedItemOffsetStart = offsetStart;
|
||||||
|
mSelectedItemOffsetEnd = offsetEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置选中的Item居中;
|
||||||
|
* 与setSelectedItemOffset()方法二选一
|
||||||
|
*
|
||||||
|
* @param isCentered
|
||||||
|
*/
|
||||||
|
public void setSelectedItemAtCentered(boolean isCentered) {
|
||||||
|
this.mSelectedItemCentered = isCentered;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getChildDrawingOrder(int childCount, int i) {
|
||||||
|
View view = getFocusedChild();
|
||||||
|
if (null != view) {
|
||||||
|
|
||||||
|
position = getChildAdapterPosition(view) - getFirstVisiblePosition();
|
||||||
|
if (position < 0) {
|
||||||
|
return i;
|
||||||
|
} else {
|
||||||
|
if (i == childCount - 1) {//这是最后一个需要刷新的item
|
||||||
|
if (position > i) {
|
||||||
|
position = i;
|
||||||
|
}
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
if (i == position) {//这是原本要在最后一个刷新的item
|
||||||
|
return childCount - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFirstVisiblePosition() {
|
||||||
|
if (getChildCount() == 0)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return getChildAdapterPosition(getChildAt(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLastVisiblePosition() {
|
||||||
|
final int childCount = getChildCount();
|
||||||
|
if (childCount == 0)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return getChildAdapterPosition(getChildAt(childCount - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***********
|
||||||
|
* 按键加载更多 start
|
||||||
|
**********/
|
||||||
|
|
||||||
|
private OnLoadMoreListener mOnLoadMoreListener;
|
||||||
|
|
||||||
|
public interface OnLoadMoreListener {
|
||||||
|
void onLoadMore();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
|
||||||
|
this.mOnLoadMoreListener = onLoadMoreListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// private PressBackListenner mPressbackLister;
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// public void setOnPressBackListener(PressBackListenner listener) {
|
||||||
|
// this.mPressbackLister = listener;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置为0,这样可以防止View获取焦点的时候,ScrollView自动滚动到焦点View的位置
|
||||||
|
*/
|
||||||
|
|
||||||
|
protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScrollStateChanged(int state) {
|
||||||
|
if (state == SCROLL_STATE_IDLE) {
|
||||||
|
// 加载更多回调
|
||||||
|
if (null != mOnLoadMoreListener) {
|
||||||
|
if (getLastVisiblePosition() >= getAdapter().getItemCount() - (1 + mLoadMoreBeforehandCount)) {
|
||||||
|
mOnLoadMoreListener.onLoadMore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LayoutManager layoutManager = getLayoutManager();
|
||||||
|
GridLayoutManager staggeredGridLayoutManager = (GridLayoutManager) layoutManager;
|
||||||
|
int spanCount = staggeredGridLayoutManager.getSpanCount();
|
||||||
|
int itemCount = staggeredGridLayoutManager.getItemCount();
|
||||||
|
Log.i(TAG,"onScrollStateChanged() spanCount="+spanCount+"itemCount="+itemCount);
|
||||||
|
if(hasResetFocus&&mScrollDirecitonUp&&lastFocusPosition>=spanCount){
|
||||||
|
Log.i(TAG,"onScrollStateChanged() hasResetFocus1="+hasResetFocus);
|
||||||
|
hasResetFocus=false;
|
||||||
|
setSelectedPosition(lastFocusPosition-spanCount);
|
||||||
|
View view = layoutManager.findViewByPosition(lastFocusPosition-spanCount);
|
||||||
|
if (view != null) {
|
||||||
|
view.requestFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(hasResetFocus&&!mScrollDirecitonUp&&lastFocusPosition<itemCount-spanCount){
|
||||||
|
Log.i(TAG,"onScrollStateChanged() hasResetFocus2="+hasResetFocus);
|
||||||
|
hasResetFocus=false;
|
||||||
|
View view = layoutManager.findViewByPosition(lastFocusPosition+spanCount);
|
||||||
|
if (view != null) {
|
||||||
|
view.requestFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(hasResetFocus&&!mScrollDirecitonUp&&lastFocusPosition>=itemCount-spanCount&&lastFocusPosition<spanCount*(itemCount/spanCount)&&itemCount%spanCount!=0){
|
||||||
|
hasResetFocus=false;
|
||||||
|
Log.i(TAG,"onScrollStateChanged() hasResetFocus3="+hasResetFocus);
|
||||||
|
View view = layoutManager.findViewByPosition(itemCount-1);
|
||||||
|
if (view != null) {
|
||||||
|
view.requestFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.onScrollStateChanged(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||||
|
|
||||||
|
boolean result = super.dispatchKeyEvent(event);
|
||||||
|
View focusView = this.getFocusedChild();
|
||||||
|
if (focusView == null) {
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
int dy = 0;
|
||||||
|
int dx = 0;
|
||||||
|
if (getChildCount() > 0) {
|
||||||
|
View firstView = this.getChildAt(0);
|
||||||
|
dy = firstView.getHeight();
|
||||||
|
dx = firstView.getWidth();
|
||||||
|
}
|
||||||
|
if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
switch (event.getKeyCode()) {
|
||||||
|
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||||
|
View rightView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_RIGHT);
|
||||||
|
Log.i(TAG, "rightView is null:" + (rightView == null));
|
||||||
|
if (rightView != null) {
|
||||||
|
rightView.requestFocus();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
this.smoothScrollBy(dx, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||||
|
View leftView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_LEFT);
|
||||||
|
Log.i(TAG, "leftView is null:" + (leftView == null));
|
||||||
|
if (leftView != null) {
|
||||||
|
leftView.requestFocus();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
this.smoothScrollBy(-dx, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||||
|
mScrollDirecitonUp=false;
|
||||||
|
View downView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_DOWN);
|
||||||
|
Log.i(TAG, " downView is null:" + (downView == null));
|
||||||
|
if (downView != null) {
|
||||||
|
hasResetFocus=false;
|
||||||
|
downView.requestFocus();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
this.smoothScrollBy(0, dy);
|
||||||
|
hasResetFocus=true;
|
||||||
|
return true;// true解决item滚动滑向中间item
|
||||||
|
}
|
||||||
|
case KeyEvent.KEYCODE_DPAD_UP:
|
||||||
|
mScrollDirecitonUp=true;
|
||||||
|
View upView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_UP);
|
||||||
|
Log.i(TAG, "upView is null:" + (upView == null));
|
||||||
|
if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (upView != null) {
|
||||||
|
hasResetFocus=false;
|
||||||
|
upView.requestFocus();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
hasResetFocus=true;
|
||||||
|
this.smoothScrollBy(0, -dy);
|
||||||
|
Log.e(TAG,"KEYCODE_DPAD_UP");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(event.getKeyCode()== KeyEvent.KEYCODE_BACK){
|
||||||
|
if(mPressbackLister!=null){
|
||||||
|
mPressbackLister.hasPressback(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Log.e(TAG,"dispatchKeyEvent()="+result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onInterceptTouchEvent(MotionEvent e) {
|
||||||
|
return super.onInterceptTouchEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置默认选中.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public void setSelectedPosition(int pos) {
|
||||||
|
this.smoothScrollToPosition(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
//防止Activity时,RecyclerView崩溃
|
||||||
|
@Override
|
||||||
|
protected void onDetachedFromWindow() {
|
||||||
|
if (getLayoutManager() != null) {
|
||||||
|
super.onDetachedFromWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否是最右边的item,如果是竖向,表示右边,如果是横向表示下边
|
||||||
|
*
|
||||||
|
* @param childPosition
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isRightEdge(int childPosition) {
|
||||||
|
LayoutManager layoutManager = getLayoutManager();
|
||||||
|
|
||||||
|
if (layoutManager instanceof GridLayoutManager) {
|
||||||
|
|
||||||
|
GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
|
||||||
|
GridLayoutManager.SpanSizeLookup spanSizeLookUp = gridLayoutManager.getSpanSizeLookup();
|
||||||
|
|
||||||
|
int totalSpanCount = gridLayoutManager.getSpanCount();
|
||||||
|
int totalItemCount = gridLayoutManager.getItemCount();
|
||||||
|
int childSpanCount = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i <= childPosition; i++) {
|
||||||
|
childSpanCount += spanSizeLookUp.getSpanSize(i);
|
||||||
|
}
|
||||||
|
if (isVertical()) {
|
||||||
|
if (childSpanCount % gridLayoutManager.getSpanCount() == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int lastColumnSize = totalItemCount % totalSpanCount;
|
||||||
|
if (lastColumnSize == 0) {
|
||||||
|
lastColumnSize = totalSpanCount;
|
||||||
|
}
|
||||||
|
if (childSpanCount > totalItemCount - lastColumnSize) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (layoutManager instanceof LinearLayoutManager) {
|
||||||
|
if (isVertical()) {
|
||||||
|
return true;
|
||||||
|
}else{
|
||||||
|
return childPosition == getLayoutManager().getItemCount() - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否是最左边的item,如果是竖向,表示左方,如果是横向,表示上边
|
||||||
|
*
|
||||||
|
* @param childPosition
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isLeftEdge(int childPosition) {
|
||||||
|
LayoutManager layoutManager = getLayoutManager();
|
||||||
|
if (layoutManager instanceof GridLayoutManager) {
|
||||||
|
GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
|
||||||
|
GridLayoutManager.SpanSizeLookup spanSizeLookUp = gridLayoutManager.getSpanSizeLookup();
|
||||||
|
|
||||||
|
int totalSpanCount = gridLayoutManager.getSpanCount();
|
||||||
|
int childSpanCount = 0;
|
||||||
|
for (int i = 0; i <= childPosition; i++) {
|
||||||
|
childSpanCount += spanSizeLookUp.getSpanSize(i);
|
||||||
|
}
|
||||||
|
if (isVertical()) {
|
||||||
|
if (childSpanCount % gridLayoutManager.getSpanCount() == 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (childSpanCount <= totalSpanCount) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (layoutManager instanceof LinearLayoutManager) {
|
||||||
|
if (isVertical()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return childPosition == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否是最上边的item,以recyclerview的方向做参考
|
||||||
|
*
|
||||||
|
* @param childPosition
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isTopEdge(int childPosition) {
|
||||||
|
LayoutManager layoutManager = getLayoutManager();
|
||||||
|
if (layoutManager instanceof GridLayoutManager) {
|
||||||
|
GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
|
||||||
|
GridLayoutManager.SpanSizeLookup spanSizeLookUp = gridLayoutManager.getSpanSizeLookup();
|
||||||
|
|
||||||
|
int totalSpanCount = gridLayoutManager.getSpanCount();
|
||||||
|
|
||||||
|
int childSpanCount = 0;
|
||||||
|
for (int i = 0; i <= childPosition; i++) {
|
||||||
|
childSpanCount += spanSizeLookUp.getSpanSize(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isVertical()) {
|
||||||
|
if (childSpanCount <= totalSpanCount) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (childSpanCount % totalSpanCount == 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} else if (layoutManager instanceof LinearLayoutManager) {
|
||||||
|
if (isVertical()) {
|
||||||
|
return childPosition == 0;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否是最下边的item,以recyclerview的方向做参考
|
||||||
|
*
|
||||||
|
* @param childPosition
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isBottomEdge(int childPosition) {
|
||||||
|
LayoutManager layoutManager = getLayoutManager();
|
||||||
|
if (layoutManager instanceof GridLayoutManager) {
|
||||||
|
GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
|
||||||
|
GridLayoutManager.SpanSizeLookup spanSizeLookUp = gridLayoutManager.getSpanSizeLookup();
|
||||||
|
int itemCount = gridLayoutManager.getItemCount();
|
||||||
|
int childSpanCount = 0;
|
||||||
|
int totalSpanCount = gridLayoutManager.getSpanCount();
|
||||||
|
for (int i = 0; i <= childPosition; i++) {
|
||||||
|
childSpanCount += spanSizeLookUp.getSpanSize(i);
|
||||||
|
}
|
||||||
|
if (isVertical()) {
|
||||||
|
//最后一行item的个数
|
||||||
|
int lastRowCount = itemCount % totalSpanCount;
|
||||||
|
if (lastRowCount == 0) {
|
||||||
|
lastRowCount = gridLayoutManager.getSpanCount();
|
||||||
|
}
|
||||||
|
if (childSpanCount > itemCount - lastRowCount) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (childSpanCount % totalSpanCount == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (layoutManager instanceof LinearLayoutManager) {
|
||||||
|
if (isVertical()) {
|
||||||
|
return childPosition == getLayoutManager().getItemCount() - 1;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public interface PressBackListenner {
|
||||||
|
void hasPressback(TvRecyclerView tvRecyclerViewer);
|
||||||
|
}
|
||||||
|
public void setOnPressBackListener(PressBackListenner listener) {
|
||||||
|
this.mPressbackLister = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
android:background="@drawable/bg">
|
android:background="@drawable/bg">
|
||||||
|
|
||||||
|
|
||||||
<com.ik.mboxlauncher.view.CustomRecyclerView
|
<com.ik.mboxlauncher.view.TvRecyclerView
|
||||||
android:id="@+id/grid_coustom_apps"
|
android:id="@+id/grid_coustom_apps"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@color/app_info_add_bg">
|
android:background="@color/app_info_add_bg">
|
||||||
<com.ik.mboxlauncher.view.CustomRecyclerView
|
<com.ik.mboxlauncher.view.TvRecyclerView
|
||||||
android:id="@+id/grid_coustom_apps"
|
android:id="@+id/grid_coustom_apps"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@color/app_info_add_bg">
|
android:background="@color/app_info_add_bg">
|
||||||
<com.ik.mboxlauncher.view.CustomRecyclerView
|
<com.ik.mboxlauncher.view.TvRecyclerView
|
||||||
android:id="@+id/grid_coustom_apps"
|
android:id="@+id/grid_coustom_apps"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
android:background="@drawable/bg">
|
android:background="@drawable/bg">
|
||||||
|
|
||||||
|
|
||||||
<com.ik.mboxlauncher.view.CustomRecyclerView
|
<com.ik.mboxlauncher.view.TvRecyclerView
|
||||||
android:id="@+id/grid_coustom_apps"
|
android:id="@+id/grid_coustom_apps"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<declare-styleable name="MultiView">
|
<declare-styleable name="MultiView">
|
||||||
<attr name="multiViewTitle" format="reference"/>
|
<attr name="multiViewTitle" format="reference"/>
|
||||||
<attr name="multiViewImage" format="reference"/>
|
<attr name="multiViewImage" format="reference"/>
|
||||||
@@ -12,4 +12,16 @@
|
|||||||
<attr name="adMultiViewTitle" format="reference"/>
|
<attr name="adMultiViewTitle" format="reference"/>
|
||||||
<attr name="adMultiViewImage" format="reference"/>
|
<attr name="adMultiViewImage" format="reference"/>
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
<declare-styleable name="TvRecyclerView">
|
||||||
|
<attr name="android:orientation" tools:ignore="ResourceName"/>
|
||||||
|
<attr name="tv_layoutManager" format="string"/>
|
||||||
|
<attr name="tv_numColumns" format="integer"/>
|
||||||
|
<attr name="tv_numRows" format="integer"/>
|
||||||
|
|
||||||
|
<attr name="tv_selectedItemCentered" format="boolean" />
|
||||||
|
<attr name="tv_loadMoreBeforehandCount" format="integer" />
|
||||||
|
<attr name="tv_selectedItemOffsetStart" format="dimension" />
|
||||||
|
<attr name="tv_selectedItemOffsetEnd" format="dimension" />
|
||||||
|
|
||||||
|
</declare-styleable>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user