So I've created a custom preference using DialogPreference - nothing terribly special; invoked via XML, runs without any major errors:
public class TimePref extends DialogPreference {
private TimePicker picker;
private int value;
private final static int DEFAULT_VALUE = 0;
public TimePref(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TimePref(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected View onCreateDialogView() {
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.gravity = Gravity.CENTER;
picker = new TimePicker(getContext());
picker.setLayoutParams(layoutParams);
FrameLayout dialogView = new FrameLayout(getContext());
dialogView.addView(picker);
return dialogView;
}
#Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
picker.setIs24HourView(true);
setTimePicker(getValue());
}
#Override
protected void onDialogClosed(boolean positiveResult) {
if (positiveResult) {
picker.clearFocus();
int newValue;
if (Build.VERSION.SDK_INT >= 23 ) {
newValue = picker.getHour() * 60;
newValue += picker.getMinute();
} else {
newValue = picker.getCurrentHour() * 60;
newValue += picker.getCurrentMinute();
}
if (callChangeListener(newValue)) {
setValue(newValue);
}
}
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getInt(index, DEFAULT_VALUE);
}
#Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
if (restorePersistedValue) {
if (defaultValue != null)
setValue((int) defaultValue);
} else {
setValue(getPersistedInt(DEFAULT_VALUE));
}
}
public void setValue(int value) {
this.value = value;
persistInt(this.value);
}
public int getValue() {
return this.value;
}
private void setTimePicker (int value) {
if (Build.VERSION.SDK_INT >= 23 ) {
picker.setHour((int) (value / 60));
picker.setMinute(value % 60);
} else {
picker.setCurrentHour((int) (value / 60));
picker.setCurrentMinute(value % 60);
}
}
}
Problem arises when I want to access it programmatically, for example:
TimePref p = (TimePref) findPreference(prefKey);
p.setValue(value);
Will return p as null and this throw a java.lang.NullPointerException. So the question is how can one access a custom preference programmatically if you cannot 'find' it?
Related
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 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);
I have a custom view PasscodeView in my dialog fragment layout.
PasscodeView.java:
public class PasscodeView extends ViewGroup {
EditText mEditText;
int mDigitCount;
private int mDigitWidth;
private int mDigitRadius;
private int mOuterStrokeWidth;
private int mInnerStrokeWidth;
private int mDigitInnerRadius;
private int mDigitSpacing;
private int mDigitElevation;
private int mControlColor;
private int mHighlightedColor;
private int mInnerColor;
private int mInnerBorderColor;
private OnFocusChangeListener mOnFocusChangeListener;
private PasscodeEntryListener mPasscodeEntryListener;
public PasscodeView(Context context) {
this(context, null);
}
public PasscodeView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PasscodeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// Get style information
TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.PasscodeView);
mDigitCount = array.getInt(R.styleable.PasscodeView_numDigits, 4);
// Dimensions
DisplayMetrics metrics = getResources().getDisplayMetrics();
mDigitRadius = array.getDimensionPixelSize(R.styleable.PasscodeView_digitRadius,
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, metrics));
mOuterStrokeWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, metrics);
mInnerStrokeWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, metrics);
mDigitInnerRadius = mDigitRadius - ((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6, metrics));
mDigitWidth = (mDigitRadius + mOuterStrokeWidth) * 2;
mDigitSpacing = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, metrics);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mDigitElevation = array.getDimensionPixelSize(R.styleable.PasscodeView_digitElevation, 0);
}
// Get theme to resolve defaults
Resources.Theme theme = getContext().getTheme();
mControlColor = Color.DKGRAY;
// Text colour, default to android:colorControlNormal from theme
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
TypedValue controlColor = new TypedValue();
theme.resolveAttribute(android.R.attr.colorControlNormal, controlColor, true);
mControlColor = controlColor.resourceId > 0 ? getResources().getColor(controlColor.resourceId) :
controlColor.data;
}
mControlColor = array.getColor(R.styleable.PasscodeView_controlColor, mControlColor);
// Accent colour, default to android:colorAccent from theme
mHighlightedColor = Color.LTGRAY;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
TypedValue accentColor = new TypedValue();
theme.resolveAttribute(R.attr.colorControlHighlight, accentColor, true);
mHighlightedColor = accentColor.resourceId > 0 ? getResources().getColor(accentColor.resourceId) :
accentColor.data;
}
mHighlightedColor = array.getColor(R.styleable.PasscodeView_controlColorActivated, mHighlightedColor);
//color for the inner circle
mInnerColor = Color.CYAN;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
TypedValue innerColor = new TypedValue();
theme.resolveAttribute(android.R.attr.colorPrimary, innerColor, true);
mInnerColor = innerColor.resourceId > 0 ? getResources().getColor(innerColor.resourceId) :
innerColor.data;
}
mInnerColor = array.getColor(R.styleable.PasscodeView_digitColorFilled, mInnerColor);
//color for the inner circle border
mInnerBorderColor = Color.GREEN;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
TypedValue innerBorderColor = new TypedValue();
theme.resolveAttribute(android.R.attr.colorPrimaryDark, innerBorderColor, true);
mInnerBorderColor = innerBorderColor.resourceId > 0 ? getResources().getColor(innerBorderColor.resourceId) :
innerBorderColor.data;
}
mInnerBorderColor = array.getColor(R.styleable.PasscodeView_digitColorBorder, mInnerBorderColor);
// Recycle the typed array
array.recycle();
// Add child views
setupViews();
}
#Override
public boolean shouldDelayChildPressedState() {
return false;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Measure children
for (int i = 0; i < getChildCount(); i++) {
getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
}
// Calculate the size of the view
int width = (mDigitWidth * mDigitCount) + (mDigitSpacing * (mDigitCount - 1));
setMeasuredDimension(
width + getPaddingLeft() + getPaddingRight() + (mDigitElevation * 2),
mDigitWidth + getPaddingTop() + getPaddingBottom() + (mDigitElevation * 2));
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// Position the child views
for (int i = 0; i < mDigitCount; i++) {
View child = getChildAt(i);
int left = i * mDigitWidth + (i > 0 ? i * mDigitSpacing : 0);
child.layout(
left + getPaddingLeft() + mDigitElevation,
getPaddingTop() + (mDigitElevation / 2),
left + getPaddingLeft() + mDigitElevation + mDigitWidth,
getPaddingTop() + (mDigitElevation / 2) + mDigitWidth);
}
// Add the edit text as a 1px wide view to allow it to focus
getChildAt(mDigitCount).layout(0, 0, 1, getMeasuredHeight());
}
private void setupViews() {
setWillNotDraw(false);
// Add a digit view for each digit
for (int i = 0; i < mDigitCount; i++) {
DigitView digitView = new DigitView(getContext(), i);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
digitView.setElevation(mDigitElevation);
}
addView(digitView);
}
// Add an "invisible" edit text to handle input
mEditText = new EditText(getContext());
mEditText.setBackgroundColor(getResources().getColor(android.R.color.transparent));
mEditText.setTextColor(getResources().getColor(android.R.color.transparent));
mEditText.setCursorVisible(false);
mEditText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(mDigitCount)});
mEditText.setInputType(InputType.TYPE_CLASS_NUMBER);
mEditText.setKeyListener(DigitsKeyListener.getInstance("1234567890"));
mEditText.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
mEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
// Update the selected state of the views
int length = mEditText.getText().length();
updateChilViewSelectionStates(length, hasFocus);
// Make sure the cursor is at the end
mEditText.setSelection(length);
// Provide focus change events to any listener
if (mOnFocusChangeListener != null) {
mOnFocusChangeListener.onFocusChange(PasscodeView.this, hasFocus);
}
}
});
mEditText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
int length = s.length();
updateChilViewSelectionStates(length, mEditText.hasFocus());
if (length == mDigitCount && mPasscodeEntryListener != null) {
mPasscodeEntryListener.onPasscodeEntered(s.toString());
}
}
});
addView(mEditText);
invalidate();
}
private void updateChilViewSelectionStates(int length, boolean hasFocus) {
for (int i = 0; i < mDigitCount; i++) {
getChildAt(i).setSelected(hasFocus && i == length);
}
}
/**
* Get the {#link Editable} from the EditText
*
* #return
*/
public Editable getText() {
return mEditText.getText();
}
/**
* Set text to the EditText
*
* #param text
*/
public void setText(CharSequence text) {
if (text.length() > mDigitCount) {
text = text.subSequence(0, mDigitCount);
}
mEditText.setText(text);
invalidateChildViews();
}
/**
* Clear passcode input
*/
public void clearText() {
mEditText.setText("");
invalidateChildViews();
}
private void invalidateChildViews() {
for (int i = 0; i < mDigitCount; i++) {
getChildAt(i).invalidate();
}
}
public void setPasscodeEntryListener(PasscodeEntryListener mPasscodeEntryListener) {
this.mPasscodeEntryListener = mPasscodeEntryListener;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
requestToShowKeyboard();
return true;
}
return super.onTouchEvent(event);
}
/**
* Requests the view to be focused and the keyboard to be popped-up
*/
public void requestToShowKeyboard() {
// Make sure this view is focused
mEditText.requestFocus();
// Show keyboard
InputMethodManager inputMethodManager = (InputMethodManager) getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.showSoftInput(mEditText, 0);
}
#Override
public OnFocusChangeListener getOnFocusChangeListener() {
return mOnFocusChangeListener;
}
#Override
public void setOnFocusChangeListener(OnFocusChangeListener l) {
mOnFocusChangeListener = l;
}
#Override
protected Parcelable onSaveInstanceState() {
Parcelable parcelable = super.onSaveInstanceState();
SavedState savedState = new SavedState(parcelable);
savedState.editTextValue = mEditText.getText().toString();
return savedState;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
SavedState savedState = (SavedState) state;
super.onRestoreInstanceState(savedState.getSuperState());
mEditText.setText(savedState.editTextValue);
mEditText.setSelection(savedState.editTextValue.length());
}
/**
* Listener that gets notified when the complete passcode has been entered
*/
public interface PasscodeEntryListener {
/**
* Called when all the digits of the passcode has been entered
*
* #param passcode - The entered passcode
*/
void onPasscodeEntered(String passcode);
}
static class SavedState extends BaseSavedState {
public static final Creator<SavedState> CREATOR =
new Creator<SavedState>() {
#Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
#Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
String editTextValue;
public SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel source) {
super(source);
editTextValue = source.readString();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(editTextValue);
}
}
class DigitView extends View {
private Paint mOuterPaint, mInnerPaint;
private int mPosition = 0;
public DigitView(Context context, int position) {
this(context);
mPosition = position;
}
public DigitView(Context context) {
this(context, null);
}
public DigitView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DigitView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
void init() {
setWillNotDraw(false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
setLayerType(LAYER_TYPE_SOFTWARE, null);
}
mOuterPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mOuterPaint.setAlpha(255);
mOuterPaint.setDither(true);
mOuterPaint.setStyle(Paint.Style.STROKE);
mOuterPaint.setStrokeWidth(mOuterStrokeWidth);
mOuterPaint.setStrokeCap(Paint.Cap.ROUND);
mOuterPaint.setStrokeJoin(Paint.Join.ROUND);
mOuterPaint.setShadowLayer(2, 0, 0, Color.parseColor("#B4999999"));
mInnerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mInnerPaint.setAlpha(255);
mInnerPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mInnerPaint.setStrokeWidth(mInnerStrokeWidth);
mInnerPaint.setStrokeCap(Paint.Cap.ROUND);
mInnerPaint.setStrokeJoin(Paint.Join.ROUND);
mInnerPaint.setColor(mInnerColor);
invalidate();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(mDigitWidth, mDigitWidth);
}
#Override
protected void onDraw(Canvas canvas) {
float center = getWidth() / 2;
if (isSelected()) {
mOuterPaint.setColor(mHighlightedColor);
} else {
mOuterPaint.setColor(mControlColor);
}
canvas.drawColor(Color.TRANSPARENT);
canvas.drawCircle(center, center, mDigitRadius, mOuterPaint);
if (mEditText.getText().length() > mPosition) {
canvas.drawCircle(center, center, mDigitInnerRadius, mInnerPaint);
}
}
}
Now, I want to open number keyboard as soon as dialog shows up. For this, I have a method requestToShowKeyboard() in PasscodeView.java to open keyboard.
But, keyboard doesn't open when dialog shows up. Below is the code I wrote inside onViewCreated().
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mPasscodeView.requestToShowKeyboard();
}
This is how I used PasscodeView in dialog fragment layout:
<com.via.android.customview.PasscodeView
android:id="#+id/edtOptCode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="#dimen/dimen_10_dp"
android:background="#null"
android:gravity="center"
android:padding="#dimen/dimen_8_dp"
passcodeView:digitColorBorder="#FFFFFF"
passcodeView:digitColorFilled="#9f9f9f"
passcodeView:digitRadius="12dp"
passcodeView:numDigits="6" />
If anyone can help me with this.
Solved: Solution to my problem has been turned out to this as below:
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mPasscodeView.post(new Runnable() {
#Override
public void run() {
mPasscodeView.requestToShowKeyboard();
}
});
}
Instead of calling in onViewCreated(), try calling like
mPasscodeView.post(new Runnable() {
#Override
public void run() {
mPasscodeView.requestToShowKeyboard();
}
});
So this will add the runnable to the queue to execute in next pass.
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)
I am using PreferenceActivity for the setting of my app.
I want to add a new preference that allow the user to select an icon.
For this task I want to use a ListPreference, but I want also to show the icon in the list.
I tried to customize the ListPreference to use a custom layout, but the problem is that once I do that the list items are not clickable (it does show my custom layout and use the default value for the current selection).
I tested it on different emulator version and on Galaxy S2. When pressing the item I could see some effect of the pressed/unpressed, but the onClick method is not called.
I followed the instruction on Android: Checkable Linear Layout for adding custom layout (I also tried the option describe in How to customize list preference radio button, but the same result).
IconTypePreference.java (copied from ListPreference and modified):
public class IconTypePreference extends DialogPreference {
private IconType value;
private int clickedDialogIndex;
private boolean valueSet;
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public IconTypePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public IconTypePreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public IconTypePreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public IconTypePreference(Context context) {
super(context);
}
public void setValue(String value) {
// Always persist/notify the first time.
final boolean changed = !TextUtils.equals(getValueText(), value);
if (changed || !valueSet) {
if (value == null) {
this.value = null;
} else {
this.value = IconType.valueOf(value);
}
valueSet = true;
persistString(value);
if (changed) {
notifyChanged();
}
}
}
public void setValueIndex(int index) {
setValue(IconType.values()[index].toString());
}
public IconType getValue() {
return value;
}
public String getValueText() {
return (value == null ? null : value.toString());
}
public int findIndexOfValue(String value) {
IconType[] values = IconType.values();
for (int i = values.length - 1; i >= 0; i--) {
if (values[i].toString().equals(value)) {
return i;
}
}
return -1;
}
private int getValueIndex() {
return findIndexOfValue(getValueText());
}
#Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
super.onPrepareDialogBuilder(builder);
clickedDialogIndex = getValueIndex();
builder.setSingleChoiceItems(new IconTypeAdapter(getContext()), clickedDialogIndex,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
clickedDialogIndex = which;
IconTypePreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
dialog.dismiss();
}
});
/*
* The typical interaction for list-based dialogs is to have
* click-on-an-item dismiss the dialog instead of the user having to
* press 'Ok'.
*/
builder.setPositiveButton(null, null);
}
#Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult && clickedDialogIndex >= 0) {
String value = IconType.values()[clickedDialogIndex].toString();
if (callChangeListener(value)) {
setValue(value);
}
}
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getString(index);
}
#Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
setValue(restoreValue ? getPersistedString(getValueText()) : (String)defaultValue);
}
#Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
if (isPersistent()) {
// No need to save instance state since it's persistent
return superState;
}
final SavedState myState = new SavedState(superState);
myState.value = getValueText();
return myState;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
if (state == null || !state.getClass().equals(SavedState.class)) {
// Didn't save state for us in onSaveInstanceState
super.onRestoreInstanceState(state);
return;
}
SavedState myState = (SavedState)state;
super.onRestoreInstanceState(myState.getSuperState());
setValue(myState.value);
}
private static class SavedState extends BaseSavedState {
String value;
public SavedState(Parcel source) {
super(source);
value = source.readString();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(value);
}
public SavedState(Parcelable superState) {
super(superState);
}
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
private static class IconTypeAdapter extends ArrayAdapter<IconType> {
private final String[] iconTypeText;
private LayoutInflater inflater;
public IconTypeAdapter(Context context) {
super(context, R.layout.icon_type_item, IconType.values());
this.inflater = LayoutInflater.from(context);
iconTypeText = context.getResources().getStringArray(R.array.icon_type);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.icon_type_item, parent, false);
}
((TextView)convertView.findViewById(R.id.text)).setText(iconTypeText[position]);
convertView.setClickable(true);
// todo: set view text
return convertView;
}
#Override
public boolean hasStableIds() {
return true;
}
#Override
public long getItemId(int position) {
return position;
}
}
}
CheckableLinearLayout.java
public class CheckableLinearLayout extends LinearLayout implements Checkable {
private Checkable checkable;
public CheckableLinearLayout(Context context) {
super(context);
}
public CheckableLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public CheckableLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CheckableLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
// setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
checkable = getCheckable(this);
if (checkable == null) {
throw new RuntimeException("Missing Checkable component");
}
}
private Checkable getCheckable(ViewGroup viewGroup) {
View v;
int childCount = viewGroup.getChildCount();
for (int i = 0; i < childCount; ++i) {
v = getChildAt(i);
if (v instanceof Checkable) {
return (Checkable)v;
} else if (v instanceof ViewGroup) {
Checkable result = getCheckable((ViewGroup)v);
if (result != null) {
return result;
}
}
}
return null;
}
#Override
public void setChecked(boolean checked) {
checkable.setChecked(checked);
}
#Override
public boolean isChecked() {
return checkable.isChecked();
}
#Override
public void toggle() {
checkable.toggle();
}
}
icon_type_item.xml
<?xml version="1.0" encoding="utf-8"?>
<com.utils.ui.widget.CheckableLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:id="#+id/text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:focusable="false"
android:focusableInTouchMode="false"/>
<RadioButton android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"/>
</com.utils.ui.widget.CheckableLinearLayout>
Added to settings.xml
<com.utils.ui.preference.IconTypePreference
android:key="icon_type"
android:defaultValue="type_b"
android:title="#string/icon_type_preference_title"/>
EDIT
There is a bug in CheckableLinearLayout.java
Replace the getCheckable method with this:
private Checkable getCheckable(ViewGroup viewGroup) {
View v;
int childCount = viewGroup.getChildCount();
for (int i = 0; i < childCount; ++i) {
v = viewGroup.getChildAt(i);
if (v instanceof Checkable) {
return (Checkable)v;
} else if (v instanceof ViewGroup) {
Checkable result = getCheckable((ViewGroup)v);
if (result != null) {
return result;
}
}
}
return null;
}
Found the solution to the problem.
The problem was in the getView method of the adapter:
I changed
convertView.setClickable(true);
to
convertView.setClickable(false);