Mimic the Overflow Spinner in a ListView Row - android

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);
}
}

Related

CheckBox touch animation on wrong position using rightDrawable

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.
}
}

Get Android default Tab indicator color

I was trying to make a tricky layout for which i need the Android's default tab indicator color.
I have searched a lot but every where I find how to change and customize tab indicator but could not find how to get color code in hex of default tab indicator.
I did some research for your question, I hope this will help you.
The tab indicator color is set in the Inner Class SlidingTabStrip of the class TabLayout (Code). Sadly you can't access this variable.
private class SlidingTabStrip extends LinearLayout {
private final Paint mSelectedIndicatorPaint;
// ...
void setSelectedIndicatorColor(int color) {
if (mSelectedIndicatorPaint.getColor() != color) {
mSelectedIndicatorPaint.setColor(color);
ViewCompat.postInvalidateOnAnimation(this);
}
}
}
But in a constructor of the TabLayout the default tab indicator color is set.
public TabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// Add the TabStrip
mTabStrip = new SlidingTabStrip(context);
addView(mTabStrip, LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabLayout, defStyleAttr, R.style.Widget_Design_TabLayout);
// <-- HERE
mTabStrip.setSelectedIndicatorColor(a.getColor(R.styleable.TabLayout_tabIndicatorColor, 0));
}
I think you need to access R.styleable.TabLayout_tabIndicatorColor to get what you want. I don't have the possibility right now to test if and how it works but I hope this helps you a bit.
Update
I tried this at home and it seems to work. I used this code in the onCreate() method of my Activity
TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.TabLayout, 0, R.style.Widget_Design_TabLayout);
// returns -16738680 in my case which is the accentColor
int color = a.getColor(R.styleable.TabLayout_tabIndicatorColor, 0);
But I saw, that R.styleable.TabLayout_tabIndicatorColor just links to the accentColor. Maybe this is the better way to get what you want.
<style name="Base.Widget.Design.TabLayout" parent="android:Widget">
<item name="tabIndicatorColor">?attr/colorAccent</item>
<!-- other items -->
</style>

Put generated ImageView in xml ImageView

I have a layout activity_main that (among other things) shows ImageView:
<ImageView
android:id="#+id/profileImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/textView2"
android:layout_alignLeft="#+id/login_button" />
I've created a class that extendsImageView and shows animated gif:
public class AnimatedGif extends ImageView
{
private Movie mMovie;
private long mMovieStart = 0;
public AnimatedGif(Context context, InputStream stream)
{
super(context);
mMovie = Movie.decodeStream(stream);
}
#Override
protected void onDraw(Canvas canvas)
{
canvas.drawColor(Color.TRANSPARENT);
super.onDraw(canvas);
final long now = SystemClock.uptimeMillis();
if (mMovieStart == 0)
{
mMovieStart = now;
}
final int realTime = (int)((now - mMovieStart) % mMovie.duration());
mMovie.setTime(realTime);
mMovie.draw(canvas, 10, 10);
this.invalidate();
}
}
In the main activity I use the following code:
setContentView(R.layout.activity_main);
.
.
.
InputStream stream = this.getResources().openRawResource(R.drawable.searching_gif);
AnimatedGif gifImageView = new AnimatedGif(this, stream);
ImageView im = (ImageView)findViewById(R.id.profileImageView);
How can I make that im will show gifImageView??
you can't. At least the way you though about it. You need some minimal changes, to load your AnimatedGif directly into the layout. The first is a constructor that takes as parameter the Context and the AttributeSet:
public AnimatedGif(Context context, AttributeSet attrs) {
}
this way you can add it directly as item of the xml, specifying the full qualified package to the class
<com.package.AnimatedGif
android:id="#+id/profileImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/textView2"
android:layout_alignLeft="#+id/login_button" />
now you probably want to have a custom attribute to specify the gif you want to load. So you could declare in your attr.xml file
<declare-styleable name="AnimatedGif">
<attr name="gifres" format="integer" />
</declare-styleable>
and inside the constructor you can load it like
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AnimatedGif);
int value = a.getInt(R.styleable.AnimatedGif_gifres, 0));
stream = context.getResources().openRawResource(value);
a.recycle();
How can I make that im will show gifImageView??
Because activity_main.xml is layout for Activity in which want to add custom ImageView using code:
1. Use LinearLayout in activity_main.xml as root Layout and set orientation to vertical
2. Assign id to root Layout of activity_main.xml like:
android:id="#+id/main_layout"
3. Get root layout in onCreate method:
LinearLayout linearLayout = (LinearLayout)findViewById(R.id.main_layout);
4. Now add gifImageView view object to linearLayout:
linearLayout.addView(gifImageView);

Can't understand Android custom drawable state

I'm new in Android development and I'm writing a small app to understand how it works. I've got all working, but at the moment I can't get a point about custom drawable states... let me explain with some sample code.
Here is my attrs.xml, in which I declare a attribute with name "oddMonth", which is boolean:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="DayView">
<attr name="oddMonth" format="boolean"/>
</declare-styleable>
</resources>
Then I have a custom View:
<?xml version="1.0" encoding="utf-8"?>
<com.example.calendar.DayView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="90dp"
android:background="#drawable/dayview_state" >
<TextView android:id="#+id/day_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:paddingRight="3dp" />
</com.example.calendar.DayView>
So I put the line "android:background="#drawable/dayview_state"", which refers to file dayview_state.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:easycalendar="http://schemas.android.com/apk/res/com.example.calendar">
<item easycalendar:oddMonth ="true" android:drawable="#drawable/customborder_odd" />
<item easycalendar:oddMonth ="false" android:drawable="#drawable/customborder_even"/>
</selector>
So far... for what I can understand.... I have a attribute defined in attrs.xml. This attribute represents the state for my custom view. According to the boolean value of this attribute my app will load one of two different xml (that are not important here), each of one defines a different drawable. So the final step is to build my custom class! Follows a extract from the class:
public class DayView extends RelativeLayout {
private static final int[] STATE_ODD_MONTH = { R.attr.oddMonth };
private boolean mOddmonth = true;
public DayView(Context mContext, AttributeSet attrs) {
super(mContext, attrs);
}
#Override
protected int[] onCreateDrawableState(int extraSpace) {
if (mOddmonth) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
mergeDrawableStates(drawableState, STATE_ODD_MONTH);
return drawableState;
} else {
return super.onCreateDrawableState(extraSpace);
}
}
public boolean isOddMonth() {
return mOddmonth;
}
public void setOddMonth(boolean oddMonth) {
if (mOddmonth != oddMonth) {
mOddmonth = oddMonth;
refreshDrawableState();
}
}
}
Ok... so I have here a private variable mOddMonth, whith getter and setter. The constructor which is used to inflate this view elsewhere. Another private variable:
private static final int[] STATE_ODD_MONTH = { R.attr.oddMonth };
which is a array made up of only one int value, that is a reference to the attribute oddMonth defined in attrs.xml. And the inherited method:
#Override
protected int[] onCreateDrawableState(int extraSpace) {
if (mOddmonth) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
mergeDrawableStates(drawableState, STATE_ODD_MONTH);
return drawableState;
} else {
return super.onCreateDrawableState(extraSpace);
}
}
which I can't really "deeply" understand... well, it seems to me that I add a state if the local variable mOddMonth is true, otherwise not. So... my code works only if I replace my dayview_state.xml with the following:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:easycalendar="http://schemas.android.com/apk/res/com.example.calendar">
<item easycalendar:oddMonth ="true" android:drawable="#drawable/customborder_odd" />
<item android:drawable="#drawable/customborder_even"/>
</selector>
In this way the first layout is loaded if THERE IS the state, otherwise will be loaded the second one. But WHAT ABOUT THE VALUE of the state? Nowhere in my code I set the value for this variable/attribute.... where I'm wrong?
I would recommend you reword your question b/c it wasn't clear what you were asking until I read your comment to #kcoppock's answer, which is -
"what i want to do (or I think I should do) is to set this value
somewhere in code according to the actual status of my custom view,
and then force it to render again.... Or I shouldn't?"
At any point, you can query the view to get it drawable state using View.getDrawableState.
If based on this, you want to re-render your drawable, then you have several options.
First of all you can call Drawable.invalidateSelf. But you rarely need to do that because usually your drawable is set as a view's background drawable which is automatically drawn for you in the draw method (not onDraw, which is what you draw). So all you need to do in that case is to invalidate the view (view.invalidate), it will automatically redraw your background drawable (hence picking up your drawable state change).
If you are using your drawable not as a background but for your main drawing then you draw your drawables in onDraw. A simple myDrawable.draw(canvas) should be enough. But remember to vall view.invalidate to trigger the onDraw method.
You're correct; you'll need to assign that value in your constructor with the AttributeSet variable:
TypedArray values = context.obtainStyledAttributes(attrs, STATE_ODD_MONTH);
boolean isOddMonth = values.getBoolean(R.attr.oddMonth, false);
mOddmonth = isOddMonth;
values.recycle();
I believe this should do the trick. I usually use a declare-styleable tag in attrs.xml instead of hardcoding an int[], but I believe it should work identically.

Need the image id from xml attributes custom widget

I have a custom control (very simple for now) that is like a button. It needs to display an unpressed and a pressed image. It appears multiple times in the activity and has different pairs of images depending on where it's used. Think of toolbar icons - similar to that.
Here's an extract of my layout:
<TableLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:MyApp="http://schemas.android.com/apk/res/com.example.mockup"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TableRow>
<com.example.mockup.ImageGestureButton
android:id="#+id/parent_arrow"
android:src="#drawable/parent_arrow"
MyApp:srcPressed="#drawable/parent_arrow_pressed"
... />
...
</TableRow>
</TableLayout>
attrs.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ImageGestureButton">
<attr name="srcPressed" format="reference" />
</declare-styleable>
</resources>
And, in the R.java, one finds:
public static final class drawable {
public static final int parent_arrow=0x7f020003;
public static final int parent_arrow_pressed=0x7f020004;
...
}
During widget instantiation, I want to determine the ids declared in the activity xml. How do I do that? I've tried this (I updated my original post with working code; so, the following works.)
public class ImageGestureButton extends ImageView
implements View.OnTouchListener
{
private Drawable unpressedImage;
private Drawable pressedImage;
public ImageGestureButton (Context context, AttributeSet attrs)
{
super(context, attrs);
setOnTouchListener (this);
unpressedImage = getDrawable();
TypedArray a = context.obtainStyledAttributes (attrs, R.styleable.ImageGestureButton, 0, 0);
pressedImage = a.getDrawable (R.styleable.ImageGestureButton_srcPressed);
}
public boolean onTouch (View v, MotionEvent e)
{
if (e.getAction() == MotionEvent.ACTION_DOWN)
{
setImageDrawable (pressedImage);
}
else if (e.getAction() == MotionEvent.ACTION_UP)
{
setImageDrawable (unpressedImage);
}
return false;
}
}
If you want to get the drawable use TypedArray.getDrawable(). In your example you are using getString().
In your declare-styleable use
<attr name="srcPressed" format="reference" />
If you want the actual resource ID for the Drawable, rather than the fully resolved Drawable it's self, you can do this:
TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.FooLayout );
TypedValue value = new TypedValue();
a.getValue( R.styleable.FooLayout_some_attr, value );
Log.d( "DEBUG", "This is the actual resource ID: " + value.resourceId );

Categories

Resources