CheckBox touch animation on wrong position using rightDrawable - android

I'm using custom checkbox for rtl support using rightDrawable property.
public class SRCheckBox extends AppCompatCheckBox {
public SRCheckBox(Context context) {
super(context);
init(context);
}
private void init(Context context) {
if (isRTL()) {
this.setButtonDrawable(null);
int[] attrs = {android.R.attr.listChoiceIndicatorMultiple};
TypedArray ta = context.getTheme().obtainStyledAttributes(attrs);
Drawable rightDrawable = ta.getDrawable(0);
this.setCompoundDrawablesWithIntrinsicBounds(null, null, rightDrawable, null);
}
}
}
but here is the problem that I'm facing with: please looke at this gif
As you can see touch animation is affecting on left side (on text) instead of
animating on the checkbox itself.
I've also tried in XML:
<CheckBox
android:id="#+id/fastDecodeCB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:button="#null" // this is causing the problem
android:drawableRight="?android:attr/listChoiceIndicatorMultiple" />
but it looks the same. any suggestions?

You are setting the checkbox button to null effectively removing it and setting a right drawable. The right drawable responds to the clicks, but the checkbox doesn't really know that the drawable is the button (you told it there is no button), so it just does what you see.
Try the following for the init method in your custom view.
private void init(Context context) {
if (isRTL()) {
// This will flip the text and the button drawable. This could also be set in XML.
setLayoutDirection(LAYOUT_DIRECTION_RTL);
int[] attrs = {android.R.attr.listChoiceIndicatorMultiple};
TypedArray ta = context.getTheme().obtainStyledAttributes(attrs);
Drawable rightDrawable = ta.getDrawable(0);
this.setButtonDrawable(rightDrawable);
ta.recycle(); // Remember to do this.
}
}

Related

Custom ImageButton, which method to override for simplest RadioButton interface?

I am in the process of making a custom view that is essentially an ImageButton with added logic so it also have the behavior of a RadioButton. All I want to do is have it built into the view that when the user clicks the button the image is changed, an internal boolean is marked true to note it is selected, and an interface method is called to let the RadioGroup it is a part of to unselect all the other views within it. I don't want to impact the existing behavior of the base ImageButton whatsoever.
I've only made one other custom view before and that was by following a tutorial almost exactly to the letter and since there are so many different methods inhereted from View that deal with clicks/touches (i.e. onTouch, onClick, motion event, etc.) taking it all in has left me a bit confused. I am fine writing the interface itself, its the modification of ImageButton where I'm not too sure how to attack it.
So, I ask you all: What method/methods do I need to override to add this simple functionality, while not impacting the current behavior of ImageButton, nor screwing up the ability to set an onTouchListener for the button that will perform additional actions on click without compromising this built in radio button logic? If I need to override something that will mess with the default behavior I mentioned, what do I need to put in the new method to restore that functionality?
This is what I have so far:
public class RadioImageButton extends AppCompatImageButton implements RadioCheckable {
//Default constructor
public RadioImageButton(Context context) {
super(context);
initView();
}
//Constructor with defined attributes
public RadioImageButton(Context context, AttributeSet attrs) {
super(context, attrs);
parseAttributes();
initView();
}
//Constructor with defined attributes and attributes taken from style defaults that aren't defined
public RadioImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//=========================================================================
// Setup
//=========================================================================
private void initView()
{
}
private void parseAttributes()
{
}
}
The approach I would like to take would be something like:
...All other code I already showed
mChecked = false;
#Overide
void onClick(...)
{
mChecked = true;
setImageSource(R.example.checked_image); // Or I can use a selector resource
*Call to Radio Interface*;
mOnTouchListener.onTouch(v, event); //Handle user onTouchListener
}
...
and leave all the other code alone, though I'm sure it isn't quite that simple.
I thought a good start would be trying to find the source code for the default ImageButton class and set mine up to be a near replica so I can understand how it works and then modify from there, but all I could really find was this:
https://android.googlesource.com/platform/frameworks/base/+/android-7.0.0_r35/core/java/android/widget/ImageButton.java
and there is no way that is the actual source because pressing Ctrl+O shows many more functions that ImageButton defines that are not inherited from another class; regardless, that link is not at all helpful as its basically a giant comment with little to no code.
Thanks for any suggestions that will help me accomplish this in the most straightforward way.
EDIT: #pskink - Looking through the code you provided, it seems like it is trying to generate a matrix in order to transform the provided drawable (src) so that it fits into a new rectangle (dst) while maintaining the aspect ratio and positioning (hence ScaleToFit.CENTER). I would assume the destination rectangle would be the bounds of the view the drawable is contained in, which in this case is the RadioButton, but while stepping through the override of the "draw()" method it doesn't quite seem to be doing that, though I'm not quite sure how cavas.concat(matrix) is resolved so I'm not positive. Regardless it doesn't seem to work as intended or I am somehow using it wrong.
While maybe not the most robust method, it seems like the most straightforward, yet effective way to handle what I wanted to do was to leverage the Matrix class and its powerful scaling/transformation tools, specifically "setRectToRect()". Creating a custom view that extends RadioButton instead of ImageButton allowed me to make use of the existing RadioGroup, while manipulating characteristics of the button's drawables in the new classes Constructor achieved the behavior I was looking for.
Custom RadioButton class:
public class RadioImageButton extends android.support.v7.widget.AppCompatRadioButton {
int stateDrawable; //Resource ID for RadioButton selector Drawable
D scaledDrawable; //Post-scaling drawable
public RadioImageButtonTwo(Context context) {
super(context);
initView();
}
public RadioImageButtonTwo(Context context, AttributeSet attrs) {
super(context, attrs);
parseAttributes(attrs);
initView();
}
private void parseAttributes(AttributeSet attrs)
{
TypedArray styledAttrs = getContext().obtainStyledAttributes(attrs,R.styleable.RadioImageButtonTwo);
try {
// Obtain selector drawable from attributes
stateDrawable = styledAttrs.getResourceId(R.styleable.RadioImageButtonTwo_button_sDrawable, R.drawable.test_draw2);
} finally {
styledAttrs.recycle(); //Required for public shared view
}
}
private void initView()
{
scaledDrawable = new D(getResources(),stateDrawable); // Create scaled drawable
setBackground(scaledDrawable); // Apply scaled drawable
setButtonDrawable(android.R.color.transparent); // "Disable" button graphic
}
}
See more on setting up a custom view here: https://developer.android.com/training/custom-views/create-view#customattr
Custom drawable class "D" that includes fitCenter scaling thanks to #pskink:
class D extends StateListDrawable {
private Rect bounds = new Rect();
private RectF src = new RectF();
private RectF dst = new RectF();
private Matrix matrix = new Matrix();
public D(Resources r, int resId) {
try {
XmlResourceParser parser = r.getXml(resId);
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.START_TAG && parser.getName().equals("selector")) {
inflate(r, parser, Xml.asAttributeSet(parser));
break;
}
}
} catch (XmlPullParserException | IOException e) {
e.printStackTrace();
}
}
#Override
public void draw(Canvas canvas) {
Drawable current = getCurrent();
bounds.set(0, 0, current.getIntrinsicWidth(), current.getIntrinsicHeight());
current.setBounds(bounds);
src.set(bounds);
dst.set(getBounds());
matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
canvas.concat(matrix);
super.draw(canvas);
}
}
Note that for whatever reason setting the button drawable itself to this custom drawable breaks the scaling, so changing the background to the custom drawable and setting the button drawable to transparent was the only way this worked. This custom drawable could easily be expanded upon to have more scaling type options and another view attribute could be defined to allow the user to choose the scaling type through XML.
This custom ImageView that mimics the (pointed out by pskink aswell) could also prove helpful in this task, as it too utilizes the Matrix class to implement multiple types of image scaling: https://github.com/yqritc/Android-ScalableImageView

Has the new Password Visibility Toggle broken existing drawableRight for EditTexts?

EDIT I just tried an EditText without a TextInputLayout and it works as expected. So the problem must be with new changes in the TextInputLayout.
I have been using a custom EditText class as child of a TextInputLayout for around a month. When the user typed, an x would appear in the drawableRight field. I have successfully displayed images for drawableLeft, drawableTop, and drawableBottom, but setting drawableRight provides me with a blank. Note: Clicking the blank space where the X SHOULD be works as expected, the text is cleared.
This first picture is how it originally looked:
Ever since upgrading to support-v4:24.2.0 the functionality has been broken. It now places the "x" where a drawable set with drawableBottom should appear. This second picture shows the new behavior:
XML Code
<android.support.design.widget.TextInputLayout
android:id="#+id/til_delivery_info_state"
android:hint="#string/state_hint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/large_margin"
android:layout_marginRight="#dimen/large_margin">
<com.example.ui.edittexts.ClearableEditText
android:id="#+id/et_state"
android:inputType="textCapWords|text|textNoSuggestions"
android:nextFocusDown="#+id/et_di_zip_code"
android:text="#={deliveryViewModel.state}"
android:gravity="center_vertical|left"
android:singleLine="true"
android:textSize="#dimen/text_size"/>
</android.support.design.widget.TextInputLayout>
Java
final Drawable drawable = ContextCompat.getDrawable(context, R.drawable.ic_clear_text_gray_x);
final Drawable wrappedDrawable = DrawableCompat.wrap(drawable);
mClearTextIcon.setBounds(0, 0, mClearTextIcon.getIntrinsicWidth(), mClearTextIcon.getIntrinsicHeight());
mClearTextIcon.setVisible(true, false);
final Drawable[] compoundDrawables = getCompoundDrawables();
setCompoundDrawablesWithIntrinsicBounds(
compoundDrawables[0],
compoundDrawables[1],
visible ? mClearTextIcon : null,
compoundDrawables[3]);
UPDATE 14 SEP 2016
A new version of support library 24.2.1 is out and this issue is marked as fixed. According to changelog
Fixed issues:
TextInputLayout overrides right compound drawable. (AOSP issue 220728)
Original answer
Warning 1
This answer will break this new password visibility toggle feature.
Warning 2
This answer may cause an unexpected behaviour after updating support lib (assuming that they will fix this issue).
Looks like TextInputLayout screws things up here, specifically these lines from updatePasswordToggleView method.
final Drawable[] compounds = TextViewCompat.getCompoundDrawablesRelative(mEditText);
TextViewCompat.setCompoundDrawablesRelative(mEditText, compounds[0], compounds[1], mPasswordToggleDummyDrawable, compounds[2]);
As you can see it sets mPasswordToggleDummyDrawable as a right drawable and then sets compounds[2] (which is your custom drawable that you want to be one the right) as a bottom drawable.
updatePasswordToggleView method is called in onMeasure method. Possible workaround is to create a custom TextInputEditText and override it's onMeasure method. Let's call it PassFixTextInputEditText
public class PassFixTextInputEditText extends TextInputEditText {
public PassFixTextInputEditText(final Context context) {
super(context);
}
public PassFixTextInputEditText(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
public PassFixTextInputEditText(final Context context, final AttributeSet attrs, final int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Drawable[] drawables = getCompoundDrawables();
setCompoundDrawables(drawables[0], drawables[1], drawables[3], null);
}
}
and use it like this
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorEnabled="true">
<com.kamilzych.temp.PassFixTextInputEditText
android:id="#+id/textInputEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:maxLength="23"/>
</android.support.design.widget.TextInputLayout>
(don't forget to change the package name)
As you can see, after TextInputLayout sets your custom drawable as bottom drawable we set it as a right one.

Changing tint color of Android EditText programmatically

I am trying to change the tinting color of an EditText View programmatically during runtime. Basically i want to change what you would usually apply as ?attr/colorControlNormal like in the default background drawable.
Changing the background tint does not correctly apply by just setting a new ColorsStateList with one color:
editText.setBackgroundTintList( ColorStateList.valueOf( color ) );
For one the result is applied to all EditText although the tint list is applied and internally mutates the drawable. Also the alpha as specified in the default background 1 is visible at the beginning.
Here is the outcome of setting the tint color on just the first EditText:
So my question would be: How can I properly apply the tint programmatically to an EditText?
This works for me:
editText.getBackground().setColorFilter(getResources().getColor(R.color.your_color),
PorterDuff.Mode.SRC_ATOP);
Source: Changing EditText bottom line color with appcompat v7
With the newly introduced android.support.v4.graphics.drawable.DrawableCompat#setTint setting the color is now possible.
Try to create a custom EditText and add this.setBackgroundTintList( ColorStateList.valueOf( color ) ); into constructor.
setColorFilter not working for me. I used:
Drawable wrappedDrawable = DrawableCompat.wrap(mView.getBackground());
DrawableCompat.setTint(wrappedDrawable, getResources().getColor(R.color.red));
mView.setBackgroundDrawable(wrappedDrawable);
or
DrawableCompat.setTint(mView.getBackground(), ContextCompat.getColor(this, R.color.red));
Let's try.
for Kotlin
editText.backgroundTintList = ColorStateList.valueOf(R.color.colorLightGray )
I wrote a small component to achieve this behavior.
Few important notes:
Old school setColorFilter method is used
To make tint work, first switch focus to other view, then tint EditText background drawable
Usage
ErrorLabelLayout layoutPassError = (ErrorLabelLayout) findViewById(R.id.layoutPasswordError)
layoutPassError.setError("Password_is_wrong");
// when you want to clear error e.g. in on text changed method
layoutPassError.clearError();
XML
<com.view.material.ErrorLabelLayout
android:id="#+id/layoutPasswordError"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="false">
<EditText
android:id="#+id/editPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:hint="Enter your password"/>
</com.view.material.ErrorLabelLayout>
Source
public class ErrorLabelLayout extends LinearLayout implements ViewGroup.OnHierarchyChangeListener {
private static final int ERROR_LABEL_TEXT_SIZE = 12;
private static final int ERROR_LABEL_PADDING = 4;
private TextView mErrorLabel;
private Drawable mDrawable;
private int mErrorColor;
public ErrorLabelLayout(Context context) {
super(context);
initView();
}
public ErrorLabelLayout(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public ErrorLabelLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView() {
setOnHierarchyChangeListener(this);
setOrientation(VERTICAL);
mErrorColor = Color.parseColor("#D32F2F");
initErrorLabel();
}
private void initErrorLabel() {
mErrorLabel = new TextView(getContext());
mErrorLabel.setFocusable(true);
mErrorLabel.setFocusableInTouchMode(true);
mErrorLabel.setTextSize(ERROR_LABEL_TEXT_SIZE);
mErrorLabel.setTextColor(mErrorColor);
mErrorLabel.setPadding(dipsToPix(ERROR_LABEL_PADDING), 0, dipsToPix(ERROR_LABEL_PADDING), 0);
}
public void setErrorColor(int color) {
mErrorColor = color;
mErrorLabel.setTextColor(mErrorColor);
}
public void clearError() {
mErrorLabel.setVisibility(INVISIBLE);
mDrawable.clearColorFilter();
}
public void setError(String text) {
mErrorLabel.setVisibility(VISIBLE);
mErrorLabel.setText(text);
// changing focus from EditText to error label, necessary for Android L only
// EditText background Drawable is not tinted, until EditText remains focus
mErrorLabel.requestFocus();
// tint drawable
mDrawable.setColorFilter(mErrorColor, PorterDuff.Mode.SRC_ATOP);
}
#Override
public void onChildViewAdded(View parent, View child) {
int childCount = getChildCount();
if (childCount == 1) {
mDrawable = getChildAt(0).getBackground();
addView(mErrorLabel);
}
}
#Override
public void onChildViewRemoved(View parent, View child) {
}
private int dipsToPix(float dps) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dps, getResources().getDisplayMetrics());
}
}
Tested on API 16 / 21 with com.android.support:appcompat-v7:22.1.1 library.

how to change image as long as button is pressed... and then showing default on button unpress android

I want to change imageview's image when user is holding button and change the image back to the default when user releases button.
Use OnFocusChangeListener(view) method & override OnFocusChange() method to this.
You need to use XML Selectors for this. You can specify whatever drawable you want for whatever state you want.
The docs are here: http://developer.android.com/reference/android/graphics/drawable/StateListDrawable.html
You will create an XML, that has references to each of the states you want to apply styles to, then you will reference when you assign a drawable to a view (a button for instance), instead of a particular drawable.
Check this SO question for more details: Android selector & text color
You can set an OnTouchListener to the ImageView and in the OnTouchListener you can set the image you want to display in MotionEvent.ACTION_DOWN and revert the old image in MotionEvent.ACTION_UP.
You can create a custom Button class like this
public class MButton extends Button {
public MButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
public MButton(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public MButton(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
// define style
/**
* Send resource ids for normal, selected, pressed in respective order
*
* #param mImageIds
*/
public void setDrawablePressedBg(Integer... mImageIds) {
StateListDrawable bg = new StateListDrawable();
Drawable normal = this.getResources().getDrawable(mImageIds[0]);
Drawable selected = this.getResources().getDrawable(mImageIds[1]);
Drawable pressed = this.getResources().getDrawable(mImageIds[2]);
bg.addState(View.PRESSED_ENABLED_STATE_SET, pressed);
bg.addState(View.ENABLED_FOCUSED_STATE_SET, selected);
bg.addState(View.ENABLED_STATE_SET, normal);
bg.addState(View.FOCUSED_STATE_SET, selected);
bg.addState(View.EMPTY_STATE_SET, normal);
this.setBackgroundDrawable(bg);
}
// define style
public void setBackgroundPressedBg(Integer p1, Integer p2, Integer p3) {
StateListDrawable bg = new StateListDrawable();
Drawable normal = this.getResources().getDrawable(p1);
Drawable selected = this.getResources().getDrawable(p2);
Drawable pressed = this.getResources().getDrawable(p3);
bg.addState(View.PRESSED_ENABLED_STATE_SET, pressed);
bg.addState(View.ENABLED_FOCUSED_STATE_SET, selected);
bg.addState(View.ENABLED_STATE_SET, normal);
bg.addState(View.FOCUSED_STATE_SET, selected);
bg.addState(View.EMPTY_STATE_SET, normal);
this.setBackgroundDrawable(bg);
}
}
And you can use it like this in your onCreate Method
this.book_appointment = (MButton) this._view
.findViewById(R.id.btn_book);
this.book_appointment.setBackgroundPressedBg(R.drawable.btnnormal,
R.drawable.btnsel, R.drawable.btnsel);
This will go like this in your xml layout file
<com.packageName.custom.MButton
android:id="#+id/btn_book"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/tv_time1"
android:layout_marginTop="20dp"
android:background="#drawable/bookapointment" />
here in this post you can find the solution:
https://stackoverflow.com/a/18196156/2000517
Take care! ;)

Mimic the Overflow Spinner in a ListView Row

I'm trying to put in a Spinner on each row of a ListView within a ListFragment.
I want it to look like a vertical overflow image like in the store but I'm not able to figure out how to show the vertical overflow image that is clickable to show the options.
It always looks like below instead. I would like to remove "Options" and have the overflow image instead.
Any help is appreciated.
Found relevant ideas from other posts and combined them, thank you Stack Overflow.
Android: How to set spinner selector to own image/icon?
Declaring a custom android UI element using XML
How to get width and height of the image?
The idea is that you create a 0dp width Spinner with an ImageView over it. When you click the image, it shows the drop down. I haven't tested it's behavior when the Spinner is at the edge of the screen yet and may very well cause trouble. I also need to tweak the position of the Spinner, but this works for now.
My plan is to catch the selection from the Spinner and then open a dialog / intent based on what was clicked. Here is what it looks like. (the ImageView is faint but it's mostly a placehodler for me right now)
Before click
After click
Here is the general code I used since this seems desirable to others.
values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="OverflowSpinner">
<attr name="imageResource" format="string" />
<attr name="spinnerTextResource" format="string" />
</declare-styleable>
</resources>
values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="spinner_array">
<item>Skip</item>
<item>View log</item>
</string-array>
</resources>
layouts/row.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:awesome="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- stuff -->
<com.blah.package.OverflowSpinner
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
awesome:imageResource="#drawable/ic_menu_moreoverflow_normal_holo_light"
awesome:spinnerTextResource="#array/spinner_array"
/>
</RelativeLayout>
OverflowSpinner.java
public class OverflowSpinner extends RelativeLayout {
int mImage;
int mStrings;
public OverflowSpinner(Context context) {
super(context);
}
public OverflowSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
setupDisplay(context);
}
public OverflowSpinner(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
setupDisplay(context);
}
private void init(AttributeSet attrs) {
TypedArray attribs = getContext().obtainStyledAttributes(attrs, R.styleable.OverflowSpinner);
// get attributes
mImage = attribs.getResourceId(R.styleable.OverflowSpinner_imageResource, -1);
mStrings = attribs.getResourceId(R.styleable.OverflowSpinner_spinnerTextResource, -1);
attribs.recycle();
}
private void setupDisplay(Context context) {
BitmapDrawable bitmap = (BitmapDrawable)this.getResources().getDrawable(mImage);
int height = bitmap.getBitmap().getHeight();
// set size of Spinner to 0 x height so it's "hidden"
// the height is used to help position the Spinner in a nicer spot
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(context, mStrings, android.R.layout.simple_spinner_item);
// setup spinner
final Spinner spinner = new Spinner(context);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
spinner.setLayoutParams(lp);
this.addView(spinner);
// set size of image to be normal
lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp.addRule(ALIGN_PARENT_BOTTOM);
ImageButton option = new ImageButton(context);
option.setBackgroundResource(android.R.color.transparent);
option.setImageResource(mImage);
option.setLayoutParams(lp);
// when clicking the image button, trigger the spinner to show
option.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
spinner.performClick();
}
});
this.addView(option);
}
}

Categories

Resources