I want to achieve Single RadioGroup like below image for blood group selection.
How can this be done?
I created my own RadioGridLayout which include RadioGroup code and extends GridLayout.
You can copy this code. For me working well.
After you can use this layout in your xml and customize like grid layout.
For R.styleable.RadioGridLayout_checked I used code like this:
<resources>
<declare-styleable name="RadioGridLayout">
<attr name="checked" format="integer" />
</declare-styleable>
</resources>
public class RadioGridLayout extends GridLayout {
private int mCheckedId = -1;
private CompoundButton.OnCheckedChangeListener mChildOnCheckedChangeListener;
private boolean mProtectFromCheckedChange = false;
private OnCheckedChangeListener mOnCheckedChangeListener;
private PassThroughHierarchyChangeListener mPassThroughListener;
private void setCheckedId(#IdRes int id) {
mCheckedId = id;
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);
}
AutofillManager afm = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
afm = getContext().getSystemService(AutofillManager.class);
}
if (afm != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
afm.notifyValueChanged(this);
}
}
}
public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
mOnCheckedChangeListener = listener;
}
public interface OnCheckedChangeListener {
void onCheckedChanged(RadioGridLayout group, #IdRes int checkedId);
}
private int mInitialCheckedId = View.NO_ID;
public RadioGridLayout(Context context) {
super(context);
setOrientation(VERTICAL);
init();
}
public RadioGridLayout(Context context, AttributeSet attrs) {
super(context, attrs);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
}
}
TypedArray attributes = context.obtainStyledAttributes(
attrs,
R.styleable.RadioGridLayout,
R.attr.radioButtonStyle, 0);
int value = attributes.getResourceId(R.styleable.RadioGridLayout_checked, View.NO_ID);
if (value != View.NO_ID) {
mCheckedId = value;
mInitialCheckedId = value;
}
attributes.recycle();
init();
}
private void init() {
mChildOnCheckedChangeListener = new CheckedStateTracker();
mPassThroughListener = new PassThroughHierarchyChangeListener();
super.setOnHierarchyChangeListener(mPassThroughListener);
}
#Override
public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
mPassThroughListener.mOnHierarchyChangeListener = listener;
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
if (mCheckedId != -1) {
mProtectFromCheckedChange = true;
setCheckedStateForView(mCheckedId, true);
mProtectFromCheckedChange = false;
setCheckedId(mCheckedId);
}
}
#Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if (child instanceof RadioButton) {
final RadioButton button = (RadioButton) child;
if (button.isChecked()) {
mProtectFromCheckedChange = true;
if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
}
mProtectFromCheckedChange = false;
setCheckedId(button.getId());
}
}
super.addView(child, index, params);
}
public void check(#IdRes int id) {
if (id != -1 && (id == mCheckedId)) {
return;
}
if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
}
if (id != -1) {
setCheckedStateForView(id, true);
}
setCheckedId(id);
}
private void setCheckedStateForView(int viewId, boolean checked) {
View checkedView = findViewById(viewId);
if (checkedView != null && checkedView instanceof RadioButton) {
((RadioButton) checkedView).setChecked(checked);
}
}
#IdRes
public int getCheckedRadioButtonId() {
return mCheckedId;
}
public void clearCheck() {
check(-1);
}
#Override
public GridLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new GridLayout.LayoutParams(getContext(), attrs);
}
#Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof RadioGroup.LayoutParams;
}
#Override
protected GridLayout.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams();
}
#Override
public CharSequence getAccessibilityClassName() {
return RadioGroup.class.getName();
}
public static class LayoutParams extends GridLayout.LayoutParams {
public LayoutParams(Spec rowSpec, Spec columnSpec) {
super(rowSpec, columnSpec);
}
public LayoutParams() {
super();
}
public LayoutParams(ViewGroup.LayoutParams params) {
super(params);
}
public LayoutParams(MarginLayoutParams params) {
super(params);
}
public LayoutParams(GridLayout.LayoutParams source) {
super(source);
}
public LayoutParams(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void setBaseAttributes(TypedArray a,
int widthAttr, int heightAttr) {
if (a.hasValue(widthAttr)) {
width = a.getLayoutDimension(widthAttr, "layout_width");
} else {
width = WRAP_CONTENT;
}
if (a.hasValue(heightAttr)) {
height = a.getLayoutDimension(heightAttr, "layout_height");
} else {
height = WRAP_CONTENT;
}
}
}
private class CheckedStateTracker implements CompoundButton.OnCheckedChangeListener {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (mProtectFromCheckedChange) {
return;
}
mProtectFromCheckedChange = true;
if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
}
mProtectFromCheckedChange = false;
int id = buttonView.getId();
setCheckedId(id);
}
}
private class PassThroughHierarchyChangeListener implements
ViewGroup.OnHierarchyChangeListener {
private ViewGroup.OnHierarchyChangeListener mOnHierarchyChangeListener;
#Override
public void onChildViewAdded(View parent, View child) {
if (parent == RadioGridLayout.this && child instanceof RadioButton) {
int id = child.getId();
if (id == View.NO_ID) {
id = View.generateViewId();
child.setId(id);
}
((RadioButton) child).setOnCheckedChangeListener(
mChildOnCheckedChangeListener);
}
if (mOnHierarchyChangeListener != null) {
mOnHierarchyChangeListener.onChildViewAdded(parent, child);
}
}
#Override
public void onChildViewRemoved(View parent, View child) {
if (parent == RadioGridLayout.this && child instanceof RadioButton) {
((RadioButton) child).setOnCheckedChangeListener(null);
}
if (mOnHierarchyChangeListener != null) {
mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
}
}
}
#Override
public void onProvideAutofillStructure(ViewStructure structure, int flags) {
super.onProvideAutofillStructure(structure, flags);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
structure.setDataIsSensitive(mCheckedId != mInitialCheckedId);
}
}
#Override
public void autofill(AutofillValue value) {
if (!isEnabled()) return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (!value.isList()) {
Timber.w(value + " could not be autofilled into " + this);
return;
}
}
int index = 0;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
index = value.getListValue();
}
final View child = getChildAt(index);
if (child == null) {
Timber.w("RadioGroup.autoFill(): no child with index %s", index);
return;
}
check(child.getId());
}
#Override
public int getAutofillType() {
return isEnabled() ? AUTOFILL_TYPE_LIST : AUTOFILL_TYPE_NONE;
}
#Override
public AutofillValue getAutofillValue() {
if (!isEnabled()) return null;
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getId() == mCheckedId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return AutofillValue.forList(i);
}
}
}
return null;
}
}
create two radio group.one is for first row and other is for second row.then add the following code in your java code
mFirstGroup = (RadioGroup) findViewById(R.id.first_group);
mSecondGroup = (RadioGroup) findViewById(R.id.second_group);
mFirstGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
if (checkedId != -1 && isChecking) {
isChecking = false;
mSecondGroup.clearCheck();
mCheckedId = checkedId;
}
isChecking = true;
}
});
mSecondGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
if (checkedId != -1 && isChecking) {
isChecking = false;
mFirstGroup.clearCheck();
mCheckedId = checkedId;
}
isChecking = true;
}
});
One possible solution is described by #Suhail k k.
Also have a look here (just more details to answer of #Suhail k k.).
I can propose another option:
1) make you own views (ImageView for example) and place them as you wish in your layout; put android:tag on each of them (serial number, for example, from 0 to 7);
2) make selector for each of them (state normal/selected);
3) at runtime put onClickListener on these items like this:
#Override
public void onClick(View v) {
images.get(currentSelected).setSelected(false);
currentSelected = (int) v.getTag();
images.get(currentSelected).setSelected(true);
}
It would be much easier for you to implement, imho.
Hope you got an idea in total, your implementation might be different :)
Related
I am getting null pointer on back press of activity where I have used the custom view. Where am I doing wrong?? Below is my custom view class:
public class PuzzleBoardView extends View implements ViewTreeObserver.OnGlobalLayoutListener {
public static final int NUM_SHUFFLE_STEPS = 40;
Comparator<PuzzleBoard> comparator = new PuzzleBoardComparator();
PriorityQueue<PuzzleBoard> queue = new PriorityQueue<>(9999, comparator);
private PuzzleActivity activity;
private PuzzleBoard puzzleBoard;
private ArrayList<PuzzleBoard> animation;
private Random random = new Random();
private Bitmap imgBitmap;
public PuzzleBoardView(Context context) {
super(context);
setMinimumWidth(50);
activity = (PuzzleActivity) context;
animation = null;
}
public void initialize(final Bitmap imageBitmap, Activity parent) {
final int width = getWidth();
imgBitmap = imageBitmap;
Log.d("PuzzleBoardView", "initialize: width=== " + width);
addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
#Override
public void onViewAttachedToWindow(View v) {
Log.d("PuzzleBoardView", "onViewAttachedToWindow=== " + width);
puzzleBoard = new PuzzleBoard(imgBitmap, getWidth());
shuffle();
}
#Override
public void onViewDetachedFromWindow(View v) {
Log.d("PuzzleBoardView", "onViewDetachedFromWindow=== ");
onDetachedFromWindow();
}
});
}
public void clearView() {
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.d("TAG", "onDraw: =====");
if (puzzleBoard != null) {
Log.d("TAG", "onDraw:111 =====");
if (animation != null && animation.size() > 0) {
puzzleBoard = animation.remove(0);
puzzleBoard.draw(canvas);
if (animation.size() == 0) {
animation = null;
puzzleBoard.reset();
Toast toast = Toast.makeText(activity, "Solved! ", Toast.LENGTH_LONG);
toast.show();
} else {
this.postInvalidateDelayed(500);
}
} else {
puzzleBoard.draw(canvas);
}
}
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
getViewTreeObserver().addOnGlobalLayoutListener(this);
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Log.d("PuzzleBoardView", "onDetachedFromWindow=== ");
getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
public void shuffle() {
if (animation == null && puzzleBoard != null) {
// Do something. Then:
ArrayList<PuzzleBoard> boards;
for (int i = 0; i <= NUM_SHUFFLE_STEPS; i++) {
boards = puzzleBoard.neighbours();
puzzleBoard = boards.get(random.nextInt(boards.size()));
}
puzzleBoard.reset();
invalidate();
queue.clear();
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (animation == null && puzzleBoard != null) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (puzzleBoard.click(event.getX(), event.getY())) {
invalidate();
if (puzzleBoard.resolved()) {
/*Toast toast = Toast.makeText(activity, "Congratulations!", Toast.LENGTH_LONG);
toast.show();*/
activity.puzzleSolved();
}
return true;
}
}
}
return super.onTouchEvent(event);
}
public void solve() {
puzzleBoard.steps = 0;
puzzleBoard.previousBoard = null;
queue.add(puzzleBoard);
PuzzleBoard prev = null;
ArrayList<PuzzleBoard> solution = new ArrayList<>();
while (!queue.isEmpty()) {
PuzzleBoard lowest = queue.poll();
if (lowest.priority() - lowest.steps != 0) {
for (PuzzleBoard toAdd : lowest.neighbours()) {
if (!toAdd.equals(prev)) {
queue.add(toAdd);
}
}
prev = lowest;
} else {
solution.add(lowest);
while (lowest != null) {
if (lowest.getPreviousBoard() == null) {
break;
}
solution.add(lowest.getPreviousBoard());
lowest = lowest.getPreviousBoard();
}
Collections.reverse(solution);
animation = solution;
invalidate();
break;
}
}
}
public PuzzleBoard getPuzzleBoard() {
return puzzleBoard;
}
public void setPuzzleBoard(PuzzleBoard puzzleBoard) {
this.puzzleBoard = puzzleBoard;
}
#Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
Log.d("PuzzleBoardView", "onAttachedToWindow: width=== " + getWidth());
puzzleBoard = new PuzzleBoard(imgBitmap, getWidth());
shuffle();
}
}
class PuzzleBoardComparator implements Comparator<PuzzleBoard> {
#Override
public int compare(PuzzleBoard first, PuzzleBoard second) {
if (first.priority() == second.priority()) {
return 0;
} else if (first.priority() < second.priority()) {
return -1;
} else {
return 1;
}
}
}
Here is my Exception
java.lang.NullPointerException: Attempt to invoke interface method 'void android.view.View$OnAttachStateChangeListener.onViewDetachedFromWindow(android.view.View)' on a null object reference
Change your code like below.
public PuzzleBoardView(Context context) {
super(context);
setMinimumWidth(50);
activity = (PuzzleActivity) context;
animation = null;
if(activity!=null)
initialize();
}
and remove initialize method from onCreate()
I have custom view (extends LinearLayout) which contains RecyclerView. When I add new items RecyclerView doesn't change size. I think problem is that my custom view doesn`t give enough space to RecyclerView.
The question: How to change heigh of custom view depends on chlid recylerview size?
If I'm wrong, then correct me please
CustomView:
public class ExpandableView extends LinearLayout {
private Settings mSettings ;
private int mExpandState;
private ValueAnimator mExpandAnimator;
private ValueAnimator mParentAnimator;
private AnimatorSet mExpandScrollAnimatorSet;
private int mExpandedViewHeight;
private boolean mIsInit = true;
private boolean isAllowedExpand = false;
private ScrolledParent mScrolledParent;
private OnExpandListener mOnExpandListener;
public ExpandableView(Context context) {
super(context);
init(null);
}
public ExpandableView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public ExpandableView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void init(AttributeSet attrs) {
setOrientation(VERTICAL);
this.setClipChildren(false);
this.setClipToPadding(false);
mExpandState = ExpandState.PRE_INIT;
mSettings = new Settings();
if(attrs!=null) {
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ExpandableView);
mSettings.expandDuration = typedArray.getInt(R.styleable.ExpandableView_expDuration, Settings.EXPAND_DURATION);
mSettings.expandWithParentScroll = typedArray.getBoolean(R.styleable.ExpandableView_expWithParentScroll,false);
mSettings.expandScrollTogether = typedArray.getBoolean(R.styleable.ExpandableView_expExpandScrollTogether,true);
typedArray.recycle();
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int childCount = getChildCount();
if(childCount!=2) {
throw new IllegalStateException("ExpandableLayout must has two child view !");
}
if(mIsInit) {
((MarginLayoutParams)getChildAt(0).getLayoutParams()).bottomMargin=0;
MarginLayoutParams marginLayoutParams = ((MarginLayoutParams)getChildAt(1).getLayoutParams());
marginLayoutParams.bottomMargin=0;
marginLayoutParams.topMargin=0;
marginLayoutParams.height = 0;
mExpandedViewHeight = getChildAt(1).getMeasuredHeight();
mIsInit =false;
mExpandState = ExpandState.CLOSED;
View view = getChildAt(0);
if (view != null){
view.setOnClickListener(v -> toggle());
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if(mSettings.expandWithParentScroll) {
mScrolledParent = Utils.getScrolledParent(this);
}
}
private int getParentScrollDistance () {
int distance = 0;
if(mScrolledParent == null) {
return distance;
}
distance = (int) (getY() + getMeasuredHeight() + mExpandedViewHeight - mScrolledParent.scrolledView.getMeasuredHeight());
for(int index = 0; index < mScrolledParent.childBetweenParentCount; index++) {
ViewGroup parent = (ViewGroup) getParent();
distance+=parent.getY();
}
return distance;
}
private void verticalAnimate(final int startHeight, final int endHeight ) {
int distance = getParentScrollDistance();
final View target = getChildAt(1);
mExpandAnimator = ValueAnimator.ofInt(startHeight,endHeight);
mExpandAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
target.getLayoutParams().height = (int) animation.getAnimatedValue();
target.requestLayout();
}
});
mExpandAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if(endHeight-startHeight < 0) {
mExpandState = ExpandState.CLOSED;
if (mOnExpandListener != null) {
mOnExpandListener.onExpand(false);
}
} else {
mExpandState=ExpandState.EXPANDED;
if(mOnExpandListener != null) {
mOnExpandListener.onExpand(true);
}
}
}
});
//todo ??????????????????????
mExpandState=mExpandState==ExpandState.EXPANDED?ExpandState.CLOSING :ExpandState.EXPANDING;
mExpandAnimator.setDuration(mSettings.expandDuration);
if(mExpandState == ExpandState.EXPANDING && mSettings.expandWithParentScroll && distance > 0) {
mParentAnimator = Utils.createParentAnimator(mScrolledParent.scrolledView, distance, mSettings.expandDuration);
mExpandScrollAnimatorSet = new AnimatorSet();
if(mSettings.expandScrollTogether) {
mExpandScrollAnimatorSet.playTogether(mExpandAnimator,mParentAnimator);
} else {
mExpandScrollAnimatorSet.playSequentially(mExpandAnimator,mParentAnimator);
}
mExpandScrollAnimatorSet.start();
} else {
mExpandAnimator.start();
}
}
public void setExpand(boolean expand) {
if (mExpandState == ExpandState.PRE_INIT) {return;}
getChildAt(1).getLayoutParams().height=expand?mExpandedViewHeight:0;
requestLayout();
mExpandState=expand?ExpandState.EXPANDED:ExpandState.CLOSED;
}
public boolean isExpanded() {
return mExpandState==ExpandState.EXPANDED;
}
public void toggle() {
if (isAllowedExpand){
if(mExpandState==ExpandState.EXPANDED) {
close();
}else if(mExpandState==ExpandState.CLOSED) {
expand();
}
}
}
public void expand() {
verticalAnimate(0,mExpandedViewHeight);
}
public void close() {
verticalAnimate(mExpandedViewHeight,0);
}
public interface OnExpandListener {
void onExpand(boolean expanded) ;
}
public void setOnExpandListener(OnExpandListener onExpandListener) {
this.mOnExpandListener = onExpandListener;
}
public void setExpandScrollTogether(boolean expandScrollTogether) {
this.mSettings.expandScrollTogether = expandScrollTogether;
}
public void setExpandWithParentScroll(boolean expandWithParentScroll) {
this.mSettings.expandWithParentScroll = expandWithParentScroll;
}
public void setExpandDuration(int expandDuration) {
this.mSettings.expandDuration = expandDuration;
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if(mExpandAnimator!=null&&mExpandAnimator.isRunning()) {
mExpandAnimator.cancel();
mExpandAnimator.removeAllUpdateListeners();
}
if(mParentAnimator!=null&&mParentAnimator.isRunning()) {
mParentAnimator.cancel();
mParentAnimator.removeAllUpdateListeners();
}
if(mExpandScrollAnimatorSet!=null) {
mExpandScrollAnimatorSet.cancel();
}
}
public void setAllowedExpand(boolean allowedExpand) {
isAllowedExpand = allowedExpand;
}
public void refreshView(){
ViewGroup.LayoutParams params = this.getLayoutParams();
Log.d("tag", "params - " + params.height);
}
}
Specify your item height inside RecyclerView Adapter like below :
LinearLayout.LayoutParams relParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
relParams.height = 100;
relParams.width = Utility.getScreenWidth(mContext);
holder.yourDesireView.setLayoutParams(relParams);
Here is the screen width calculator method :
public static int getScreenWidth(Context context) {
if (context == null) {
return 0;
}
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return metrics.widthPixels;
}
I think it will be helpful...
I am using this library to make a expandable RecyclerView
I made my own version of it so I can define the header but It's giving me I think I made something wrong because It's giving me this error.
What I want is:
a way to make my own header layout.
Is there a way to populate the adapter of the RecyclerView so that I don't have to create a new adapter file every time I want to implement the RecyclerView.
How to set my one item or more to be expanded as I open the activity.
My version of ExpandableRecyclerView:
public class ExpandableRecyclerView extends RecyclerView {
public ExpandableRecyclerView(Context context) {
super(context, null);
initRecycler();
}
public ExpandableRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
initRecycler();
}
public ExpandableRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initRecycler();
}
private void initRecycler() {
setClipToPadding(false);
setItemAnimator(new DefaultItemAnimator());
}
#Override
public Parcelable onSaveInstanceState() {
//begin boilerplate code that allows parent classes to save state
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
//end
if (getAdapter() != null)
ss.stateToSave = ((Adapter) this.getAdapter()).getExpandedGroups();
return ss;
}
#Override
public void onRestoreInstanceState(Parcelable state) {
//begin boilerplate code so parent classes can restore state
if (!(state instanceof SavedState)) // if state is not instance of out SaveState just restore in reg way
{
super.onRestoreInstanceState(state);
return;
}
// else if cast him to SavedState
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
//end
if (getAdapter() != null)
((Adapter) getAdapter()).setExpandedGroups(ss.stateToSave);
}
#Override
public void setAdapter(RecyclerView.Adapter adapter) {
if (!(adapter instanceof Adapter))
throw new IllegalArgumentException("adapter has to be of type ExpandableRecyclerView.Adapter");
super.setAdapter(adapter);
}
public interface OnChildItemClickedListener {
void onChildItemClicked(int group, int position);
}
static class SavedState implements Parcelable {
public static final SavedState EMPTY_STATE = new SavedState() {
};
//required field that makes Parcelables from a Parcel
public static final Creator<SavedState> CREATOR =
new Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
SparseBooleanArray stateToSave;
Parcelable superState;
SavedState() {
superState = null;
}
SavedState(Parcelable superState) {
this.superState = superState != EMPTY_STATE ? superState : null;
}
private SavedState(Parcel in) {
Parcelable superState = in.readParcelable(ExpandableRecyclerView.class.getClassLoader());
this.superState = superState != null ? superState : EMPTY_STATE;
this.stateToSave = in.readSparseBooleanArray();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(#NonNull Parcel out, int flags) {
out.writeParcelable(superState, flags);
out.writeSparseBooleanArray(this.stateToSave);
}
public Parcelable getSuperState() {
return superState;
}
}
public static abstract class Adapter<CVH extends ViewHolder, GVH extends ViewHolder, C, G> extends RecyclerView.Adapter<ViewHolder> {
private static final int TYPE_HEADER = 0;
SparseBooleanArray expanded = new SparseBooleanArray();
private OnChildItemClickedListener onChildItemClickedListener;
public Adapter() {
}
boolean isExpanded(int group) {
return expanded.get(group);
}
SparseBooleanArray getExpandedGroups() {
return expanded;
}
public void setExpandedGroups(SparseBooleanArray expanded) {
this.expanded = expanded;
}
public void expand(int group) {
if (isExpanded(group))
return;
// this lines of code calculate number of shown item in recycler view. also group is counting .
int position = 0;
for (int i = 0; i < group; i++) {
position++;
if (isExpanded(i))
position += getChildItemCount(i);
}
position++; // this for percent group
notifyItemRangeInserted(position, getChildItemCount(group)); // notify recycler view for expanding
expanded.put(group, true); // save expanding in sparce array
}
public void collapse(int group) {
if (!isExpanded(group)) // if is not expanded . so nothing to collapse.
return;
int position = 0;
for (int i = 0; i < group; i++) {
position++;
if (isExpanded(i))
position += getChildItemCount(i); // item
}
position++;
notifyItemRangeRemoved(position, getChildItemCount(group));
expanded.put(group, false);
}
public abstract int getGroupItemCount();
public abstract int getChildItemCount(int group);
#Override
public int getItemCount() {
int count = 0;
for (int i = 0; i <= getGroupItemCount(); i++) {
count += isExpanded(i) ? getChildItemCount(i) + 1 : 1;
}
return count;
}
public abstract G getGroupItem(int position);
public abstract C getChildItem(int group, int position);
public Object getItem(int i) {
int group = 0;
while (group <= getGroupItemCount()) {
if (i > 0 && !isExpanded(group)) {
i--;
group++;
continue;
}
if (i > 0 && isExpanded(group)) {
i--;
if (i < getChildItemCount(group))
return getChildItem(group, i);
i -= getChildItemCount(group);
group++;
continue;
}
if (i == 0)
return getGroupItem(group);
}
throw new IndexOutOfBoundsException();
}
#Override
public void onBindViewHolder(ViewHolder holder, int i) {
int group = 0;
while (group <= getGroupItemCount()) {
if (i > 0 && !isExpanded(group)) {
i--;
group++;
continue;
}
if (i > 0 && isExpanded(group)) {
i--;
if (i < getChildItemCount(group)) {
onBindChildViewHolder((CVH) holder, group, i);
return;
}
i -= getChildItemCount(group);
group++;
continue;
}
if (i == 0) {
onBindGroupViewHolder((GVH) holder, group);
return;
}
}
throw new IndexOutOfBoundsException();
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return viewType == TYPE_HEADER ? onCreateGroupViewHolder(parent) : onCreateChildViewHolder(parent, viewType);
}
protected abstract GVH onCreateGroupViewHolder(ViewGroup parent);
protected abstract CVH onCreateChildViewHolder(ViewGroup parent, int viewType);
public abstract int getChildItemViewType(int group, int position);
#Override
public int getItemViewType(int i) {
int group = 0;
while (group <= getGroupItemCount()) {
if (i > 0 && !isExpanded(group)) {
i--;
group++;
continue;
}
if (i > 0 && isExpanded(group)) {
i--;
if (i < getChildItemCount(group))
return getChildItemViewType(group, i);
i -= getChildItemCount(group);
group++;
continue;
}
if (i == 0)
return TYPE_HEADER;
}
throw new IndexOutOfBoundsException();
}
public void setOnChildItemClickedListener(OnChildItemClickedListener onItemClickedListener) {
this.onChildItemClickedListener = onItemClickedListener;
}
public void onBindChildViewHolder(CVH holder, final int group, final int position) {
holder.itemView.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (Adapter.this.onChildItemClickedListener != null) {
Adapter.this.onChildItemClickedListener.onChildItemClicked(group, position);
}
}
});
}
public void onBindGroupViewHolder(final GVH holder, final int group) {
if (holder instanceof GroupViewHolder)
((GroupViewHolder) holder).setExpanded(isExpanded(group));
holder.itemView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (isExpanded(group)) {
collapse(group);
if (holder instanceof GroupViewHolder)
((GroupViewHolder) holder).collapse();
} else {
expand(group);
if (holder instanceof GroupViewHolder)
((GroupViewHolder) holder).expand();
}
}
});
}
}
public static abstract class GroupViewHolder extends ViewHolder {
public GroupViewHolder(View itemView) {
super(itemView);
}
public abstract void expand();
public abstract void collapse();
public abstract boolean isExpanded();
public abstract void setExpanded(boolean expanded);
}
public static class SimpleGroupViewHolder extends GroupViewHolder {
ImageView expandedIndicator;
TextView text;
private boolean expanded;
public SimpleGroupViewHolder(Context context) {
super(View.inflate(context, R.layout.group_header, null));
itemView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
expandedIndicator = (ImageView) itemView.findViewById(R.id.carbon_groupExpandedIndicator);
text = (TextView) itemView.findViewById(R.id.carbon_groupText);
}
public SimpleGroupViewHolder(Context context, int layout) {
super(View.inflate(context, layout, null));
itemView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
expandedIndicator = (ImageView) itemView.findViewById(R.id.carbon_groupExpandedIndicator);
text = (TextView) itemView.findViewById(R.id.carbon_groupText);
}
public void expand() {
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
animator.setInterpolator(new DecelerateInterpolator());
animator.setDuration(200);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
ViewHelper.setRotation(expandedIndicator, 180 * (float) (animation.getAnimatedValue()));
expandedIndicator.postInvalidate();
}
});
animator.start();
expanded = true;
}
public void collapse() {
ValueAnimator animator = ValueAnimator.ofFloat(1, 0);
animator.setInterpolator(new DecelerateInterpolator());
animator.setDuration(200);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
ViewHelper.setRotation(expandedIndicator, 180 * (float) (animation.getAnimatedValue()));
expandedIndicator.postInvalidate();
}
});
animator.start();
expanded = false;
}
#Override
public boolean isExpanded() {
return expanded;
}
public void setExpanded(boolean expanded) {
ViewHelper.setRotation(expandedIndicator, expanded ? 180 : 0);
this.expanded = expanded;
}
public String getText() {
return text.getText().toString();
}
public void setText(String t) {
text.setText(t);
}
}
}
I got an answer for these questions:
How to set my one item or more to be expanded as I open the activity.
before setting the adapter for the RecyclerView I used expand() method as the following:
RecyclerView rv = (RecyclerView) findViewById(R.id.rv);
MyAdapter adapter = new MyAdapter(getContext(), R.layout.group_header);
adapter.expand(0); // 0 will expand the first Item.
rv.setAdapter(adapter);
a way to make my own header layout.
all you need to do is create your own group header as the following:
static class GroupViewHolder extends ExpandableRecyclerView.GroupViewHolder {
ImageView expandedIndicator;
TextView text1;
TextView text2;
private boolean expanded;
GroupViewHolder(Context context, int layout) {
super(View.inflate(context, layout, null));
itemView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
expandedIndicator = (ImageView) itemView.findViewById(R.id.groupExpandedIndicator);
text1 = (TextView) itemView.findViewById(R.id.text1);
text2 = (TextView) itemView.findViewById(R.id.text2);
}
public void expand() {
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
animator.setInterpolator(new DecelerateInterpolator());
animator.setDuration(200);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
ViewHelper.setRotation(expandedIndicator, 180 * (float) (animation.getAnimatedValue()));
expandedIndicator.postInvalidate();
}
});
animator.start();
expanded = true;
}
public void collapse() {
ValueAnimator animator = ValueAnimator.ofFloat(1, 0);
animator.setInterpolator(new DecelerateInterpolator());
animator.setDuration(200);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
ViewHelper.setRotation(expandedIndicator, 180 * (float) (animation.getAnimatedValue()));
expandedIndicator.postInvalidate();
}
});
animator.start();
expanded = false;
}
#Override
public boolean isExpanded() {
return expanded;
}
public void setExpanded(boolean expanded) {
ViewHelper.setRotation(expandedIndicator, expanded ? 180 : 0);
this.expanded = expanded;
}
void setText1(String t) {
text1.setText(t);
}
void setText2(String t) {
text2.setText(t);
}
}
I want to create a animation in android with Gridview. The animation will be when I will change the number of columns from 2 to 4.
I used the following line to change the number of columns:
If (true)
gridView.setNumColumns(2);
Else
gridView.setNumColumns(4);
I want to achieve animation like this:
https://www.youtube.com/watch?v=1NkuChdWA_I
I need it too and then i created the following class:
/**
* Created by Butzke on 19/05/2017.
*/
public class GridViewAnimated extends GridView {
private int animationDuration = 300, nextColumns;
private boolean animating = false, waitingScroll, shouldWait = true;
private GridViewAnimationListener animationListener;
public GridViewAnimated(Context context) {
super(context);
init();
}
public GridViewAnimated(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public GridViewAnimated(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
#TargetApi(21)
public GridViewAnimated(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
#Override
public int computeVerticalScrollOffset() {
return super.computeVerticalScrollOffset();
}
private void init() {
this.setStretchMode(GridView.STRETCH_COLUMN_WIDTH);
}
public void setAnimating(boolean animating) {
this.animating = animating;
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
if (waitingScroll) {
super.onScrollChanged(l, t, oldl, oldt);
if (computeVerticalScrollOffset() == 0) {
setEnabled(true);
waitingScroll = false;
setAnimating(false);
setNumColumns(nextColumns);
}
} else if (!animating) {
super.onScrollChanged(l, t, oldl, oldt);
}
}
public int getAnimationDuration() {
return animationDuration;
}
public void setAnimationDuration(int animationDuration) {
this.animationDuration = animationDuration;
}
public interface GridViewAnimationListener {
void onAnimationStart(Animator animator);
void onAnimationEnd(Animator animator);
void onAnimationCancel(Animator animator);
void onAnimationRepeat(Animator animator);
}
public GridViewAnimationListener getAnimationListener() {
return animationListener;
}
public void setAnimationListener(GridViewAnimationListener animationListener) {this.animationListener = animationListener;}
public void removeAt(int... positions) {
ArrayList<MyInteger> ints = new ArrayList<>();
for (Integer pos : positions) {
ints.add(new MyInteger(pos));
}
Collections.sort(ints);
int lowest = 99999, highest = 0;
for (MyInteger pos : ints) {
lowest = lowest < pos.getValue() ? lowest : pos.getValue();
highest = highest > pos.getValue() ? highest : pos.getValue();
}
if (lowest >= 0 && highest <= getChildCount()) {
ViewsVO originalViews = getOriginalViews();
for (MyInteger pos : ints) {
getAdapter().removeAt(pos.getValue());
}
getAdapter().notifyDataSetChanged();
animateChildViews(originalViews);
}
}
#Override
public void setAdapter(ListAdapter adapter) {
if (adapter instanceof GridViewAnimatedAdapter) {
super.setAdapter(adapter);
} else {
Log.e("GridViewAnimated", "Adapter needs to be instance of GridViewAnimatedAdapter");
Toast.makeText(getContext(), "Adapter needs to be instance of GridViewAnimatedAdapter", Toast.LENGTH_SHORT).show();
}
}
#Override
public GridViewAnimatedAdapter getAdapter() {
return (GridViewAnimatedAdapter) super.getAdapter();
}
#Override
public void setNumColumns(int numColumns) {
if (getAdapter() == null || !getAdapter().hasStableIds() || getChildCount() == 0) {
super.setNumColumns(numColumns);
setAnimating(false);
return;
} else if (animating) {
return;
}
setAnimating(true);
if (computeVerticalScrollOffset() > 0) {
waitingScroll = true;
setEnabled(false);
smoothScrollToPosition(0);
nextColumns = numColumns;
return;
}
ViewsVO originalViews = getOriginalViews();
super.setNumColumns(numColumns);
getAdapter().notifyDataSetChanged();
animateChildViews(originalViews);
}
private ViewsVO getOriginalViews() {
ViewsVO originalViews = new ViewsVO();
for (int i = 0; i < getChildCount(); i++) {
originalViews.addView(getChildAt(i));
}
return originalViews;
}
private void animateChildViews(final ViewsVO originalViews) {
final GridViewAnimated gridView = this;
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
gridView.getViewTreeObserver().removeOnPreDrawListener(this);
ViewsVO newViews = new ViewsVO();
if (Build.VERSION.SDK_INT >= 21) {
for (int i = 0, z = getChildCount() - 1; z >= 0; z--, i++) {
gridView.getChildAt(z).setTranslationZ(i);
}
}
boolean hasFirst = false;
View firstView = null, lastView = null;
float firstHeight = 0, lastHeight = 0;
ViewsVO newViewsFirstTime = new ViewsVO();
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
ViewVO nView = newViews.addView(view);
ViewVO oView = originalViews.getView(view.getId());
if (oView != null) {
view.setScaleX(getScaleX(oView, nView));
view.setScaleY(getScaleY(oView, nView));
view.setTranslationX(getTranslateX(oView, nView));
view.setTranslationY(getTranslateY(oView, nView));
if (!hasFirst) {
firstView = view;
firstHeight = oView.getHeight();
hasFirst = true;
}
lastView = view;
lastHeight = oView.getHeight();
animateView(view);
} else {
newViewsFirstTime.addView(nView);
}
}
for (int i = 0; i < newViewsFirstTime.size(); i++) {
try {
View view = newViewsFirstTime.getViews().get(i).getView();
view.getId();
view.setScaleX(view.getId() > firstView.getId() ? firstView.getScaleX() : lastView.getScaleX());
view.setScaleY(view.getId() > firstView.getId() ? firstView.getScaleY() : lastView.getScaleX());
view.setTranslationX(view.getId() > firstView.getId() ? firstView.getTranslationX() : lastView.getTranslationX());
view.setTranslationY(view.getId() > firstView.getId() ? 0 - firstHeight : lastView.getTranslationY() + lastHeight);
animateView(view);
} catch (Exception ex) {
ex.printStackTrace();
}
}
return false;
}
});
}
private void animateView(final View view) {
ViewPropertyAnimator animator = view.animate().setDuration(animationDuration).translationX(0).translationY(0).scaleX(1).scaleY(1).setListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animator) {animationListener.onAnimationStart(animator);}
#Override
public void onAnimationEnd(Animator animator) {
setAnimating(false);
animationListener.onAnimationEnd(animator);
ViewGroup.LayoutParams params = view.getLayoutParams();
params.width = ViewGroup.LayoutParams.MATCH_PARENT;
params.height = ViewGroup.LayoutParams.MATCH_PARENT;
view.setLayoutParams(params);
}
#Override
public void onAnimationCancel(Animator animator) {animationListener.onAnimationCancel(animator);}
#Override
public void onAnimationRepeat(Animator animator) {animationListener.onAnimationRepeat(animator);}
});
if (Build.VERSION.SDK_INT >= 21) {
animator.translationZ(0);
}
}
private float getTranslateX(ViewVO ov, ViewVO nv) {return ov.getPosX() - nv.getPosX() - ((ov.getWidth() < nv.getWidth() ? nv.getWidth() - ov.getWidth() : ov.getWidth() - nv.getWidth()) * (ov.getWidth() < nv.getWidth() ? 0.5f : -0.5f));}
private float getTranslateY(ViewVO ov, ViewVO nv) {return ov.getPosY() - nv.getPosY() - ((ov.getHeight() < nv.getHeight() ? nv.getHeight() - ov.getHeight() : ov.getHeight() - nv.getHeight()) * (ov.getHeight() < nv.getHeight() ? 0.5f : -0.5f));}
private float getScaleY(ViewVO ov, ViewVO nv) {
return ov.getHeight() / nv.getHeight();
}
private float getScaleX(ViewVO ov, ViewVO nv) {
return ov.getWidth() / nv.getWidth();
}
private class ViewsVO {
private ArrayList<ViewVO> views;
private int size() {
return views.size();
}
private ViewsVO() {
views = new ArrayList<>();
}
private ViewVO addView(View view) {
ViewVO v = new ViewVO(view);
addView(v);
return v;
}
private void addView(ViewVO view) {
views.add(view);
}
private ViewVO getView(long id) {
for (ViewVO view : views) {
if (view.getId() == id) {
return view;
}
}
return null;
}
private ArrayList<ViewVO> getViews() {
return views;
}
}
private class ViewVO {
private View view;
private int id;
private float posY;
private float posX;
private float width;
private float height;
private ViewVO(View view) {
this.view = view;
id = view.getId();
posX = view.getLeft();
posY = view.getTop();
width = view.getWidth();
height = view.getHeight();
}
public int getId() {
return id;
}
public View getView() {
return view;
}
private float getWidth() {
return width;
}
private float getHeight() {
return height;
}
private float getPosY() {
return posY;
}
private float getPosX() {
return posX;
}
}
public static class GridViewAnimatedAdapter extends ArrayAdapter {
private List objects;
public GridViewAnimatedAdapter(Context context, int resource, List objects) {
super(context, resource, objects);
this.objects = objects;
}
public void removeAt(int pos) {objects.remove(pos);}
#Override
public boolean hasStableIds() {
return true;
}
public View dealViewToAnimation(View view) {
ViewGroup.LayoutParams params = view.getLayoutParams();
params.width = ViewGroup.LayoutParams.MATCH_PARENT;
params.height = ViewGroup.LayoutParams.MATCH_PARENT;
view.setLayoutParams(params);
return view;
}
}
private class MyInteger implements Comparable<MyInteger> {
private int value;
public MyInteger(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
#Override
public int compareTo(MyInteger another) {return value > another.getValue() ? -1 : (value == another.getValue() ? 0 : 1);}}
}
And to use it is quite simple,
The adapter of GridView needs to extends GridViewAnimatedAdapter and:
getView(): To set an ID to each view and in the end of the method getView, needs to finish using the method dealViewToAnimation(View view) Ex:
#Override
public View getView(int position, View view, ViewGroup parent) {
...
view.setId(images.getId());
...
return dealViewToAnimation(view);
}
You can also set a listener to animation status:
gridView.setAnimationListener(new GridViewAnimated.GridViewAnimationListener() {
#Override
public void onAnimationStart(Animator animator) {
mListMenu.setVisible(false);
}
#Override
public void onAnimationEnd(Animator animator) {
mListMenu.setVisible(true);
gridView.setAnimating(false);
}
#Override
public void onAnimationCancel(Animator animator) {}
#Override
public void onAnimationRepeat(Animator animator) {
}
});
With this GridView you can animate changes in column number and animate removal of itens...
Animating change of column number:
gridView.setNumColumns(5);
Animating removal of itens:
gridView.removeAt(2, 7, 4);
gridView.removeAt(2);
When more than one, doesn't matter the order, the method will check the order and remove from the highest to lowest, considering that the highest id (that image added to the view) is the first view...
But despite it doesn't have the scale proportion animation like in the GridViewAnimated, it's better with RecyclerView, GridLayoutManager and DefaultItemAnimator, since it changes columns number and doesn't scroll...
GridLayoutmanager gridLayoutManager = new GridLayoutManager(context, 2);
recyclerView.setLayoutManager(gridLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
And then when you need to change the columns:
gridLayoutManager.requestSimpleAnimationsInNextLayout();
gridLayoutManager.setSpanCount(newNumberOfColumns);
How can i set the default MenuItem for the official BottomNavigationView (com.android.support:design:25.0.1)?
If I call programmatically menuItem.setCheckable(true).setChecked(true) the zoom effect is not performed and the BottomNavigationView shows like this:
There is more simpler way to do this since Android Support Library 25.3.0 :
bottomNavigationView.setSelectedItemId(R.id.id_of_item_you_want_to_select_as_default);
I achieved this in a much simpler way:
//R.id.bottom_bar_icon is the icon you would like clicked by default
bottomNavigationView.getMenu().performIdentifierAction(R.id.bottom_bar_icon, 0);
//set the corresponding menu item to checked = true
//and the other items to checked = false
bottomNavigationView.getMenu().getItem(0).setChecked(false);
bottomNavigationView.getMenu().getItem(1).setChecked(true);
bottomNavigationView.getMenu().getItem(2).setChecked(false);
In the end I was able to achieve this issue extending the BottomNavigationView like this:
public class RichBottomNavigationView extends BottomNavigationView {
private ViewGroup mBottomItemsHolder;
private int mLastSelection = INVALID_POSITION;
private Drawable mShadowDrawable;
private boolean mShadowVisible = true;
private int mWidth;
private int mHeight;
private int mShadowElevation = 2;
public RichBottomNavigationView(Context context) {
super(context);
init();
}
public RichBottomNavigationView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public RichBottomNavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
;
}
private void init() {
mShadowDrawable = ContextCompat.getDrawable(getContext(), R.drawable.shadow);
if (mShadowDrawable != null) {
mShadowDrawable.setCallback(this);
}
setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
setShadowVisible(true);
setWillNotDraw(false);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h + mShadowElevation, oldw, oldh);
mWidth = w;
mHeight = h;
updateShadowBounds();
}
private void updateShadowBounds() {
if (mShadowDrawable != null && mBottomItemsHolder != null) {
mShadowDrawable.setBounds(0, 0, mWidth, mShadowElevation);
}
ViewCompat.postInvalidateOnAnimation(this);
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (mShadowDrawable != null && mShadowVisible) {
mShadowDrawable.draw(canvas);
}
}
public void setShadowVisible(boolean shadowVisible) {
setWillNotDraw(!mShadowVisible);
updateShadowBounds();
}
public int getShadowElevation() {
return mShadowVisible ? mShadowElevation : 0;
}
public int getSelectedItem() {
return mLastSelection = findSelectedItem();
}
#CallSuper
public void setSelectedItem(int position) {
if (position >= getMenu().size() || position < 0) return;
View menuItemView = getMenuItemView(position);
if (menuItemView == null) return;
MenuItemImpl itemData = ((MenuView.ItemView) menuItemView).getItemData();
itemData.setChecked(true);
boolean previousHapticFeedbackEnabled = menuItemView.isHapticFeedbackEnabled();
menuItemView.setSoundEffectsEnabled(false);
menuItemView.setHapticFeedbackEnabled(false); //avoid hearing click sounds, disable haptic and restore settings later of that view
menuItemView.performClick();
menuItemView.setHapticFeedbackEnabled(previousHapticFeedbackEnabled);
menuItemView.setSoundEffectsEnabled(true);
mLastSelection = position;
}
#Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
BottomNavigationState state = new BottomNavigationState(superState);
mLastSelection = getSelectedItem();
state.lastSelection = mLastSelection;
return state;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof BottomNavigationState)) {
super.onRestoreInstanceState(state);
return;
}
BottomNavigationState bottomNavigationState = (BottomNavigationState) state;
mLastSelection = bottomNavigationState.lastSelection;
dispatchRestoredState();
super.onRestoreInstanceState(bottomNavigationState.getSuperState());
}
private void dispatchRestoredState() {
if (mLastSelection != 0) { //Since the first item is always selected by the default implementation, dont waste time
setSelectedItem(mLastSelection);
}
}
private View getMenuItemView(int position) {
View bottomItem = mBottomItemsHolder.getChildAt(position);
if (bottomItem instanceof MenuView.ItemView) {
return bottomItem;
}
return null;
}
private int findSelectedItem() {
int itemCount = getMenu().size();
for (int i = 0; i < itemCount; i++) {
View bottomItem = mBottomItemsHolder.getChildAt(i);
if (bottomItem instanceof MenuView.ItemView) {
MenuItemImpl itemData = ((MenuView.ItemView) bottomItem).getItemData();
if (itemData.isChecked()) return i;
}
}
return INVALID_POSITION;
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
mBottomItemsHolder = (ViewGroup) getChildAt(0);
updateShadowBounds();
//This sucks.
MarginLayoutParams layoutParams = (MarginLayoutParams) mBottomItemsHolder.getLayoutParams();
layoutParams.topMargin = (mShadowElevation + 2) / 2;
}
static class BottomNavigationState extends BaseSavedState {
public int lastSelection;
#RequiresApi(api = Build.VERSION_CODES.N)
public BottomNavigationState(Parcel in, ClassLoader loader) {
super(in, loader);
lastSelection = in.readInt();
}
public BottomNavigationState(Parcelable superState) {
super(superState);
}
#Override
public void writeToParcel(#NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(lastSelection);
}
public static final Parcelable.Creator<NavigationView.SavedState> CREATOR
= ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks<NavigationView.SavedState>() {
#Override
public NavigationView.SavedState createFromParcel(Parcel parcel, ClassLoader loader) {
return new NavigationView.SavedState(parcel, loader);
}
#Override
public NavigationView.SavedState[] newArray(int size) {
return new NavigationView.SavedState[size];
}
});
}
}
and calling setSelectedItem(2)