SeekBarPreference seekBarIncrement - android

I want to set seekBarIncrement in an xml (rather than programmatically). I have tried many variants of adding it to my xml, including in a style, as seekBarIncrement="100", asp:seekBarIncrement="100" etc. Nothing breaks/complains, but there is no increment either --- the seekbar values all differ only by 1, not 100, and if I put a log in code, it hasn't seen any increase there either.
How do I get seekBarIncrement to take effect? I'm using android.support.v7.preference.SeekBarPreference.
(I extended the class and do see its value, it just doesn't show anywhere or seem to affect anything)
For reference, the Support Library defines the following:
<declare-styleable name="SeekBarPreference">
<attr format="integer" name="min"/>
<attr name="android:max"/>
<attr name="android:layout"/>
<attr format="integer" name="seekBarIncrement"/>
<attr format="boolean" name="adjustable"/>
<attr format="boolean" name="showSeekBarValue"/>
</declare-styleable>
which one can see set in the SeekBarPreference class itself:
TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.SeekBarPreference, defStyleAttr, defStyleRes);
mMin = a.getInt(R.styleable.SeekBarPreference_min, 0);
setMax(a.getInt(R.styleable.SeekBarPreference_android_max, 100));
setSeekBarIncrement(a.getInt(R.styleable.SeekBarPreference_seekBarIncrement, 0));
mAdjustable = a.getBoolean(R.styleable.SeekBarPreference_adjustable, true);
mShowSeekBarValue = a.getBoolean(R.styleable.SeekBarPreference_showSeekBarValue, true);

I added some math to OnPreferenceChangeListener that uses the defined increment to round the value properly.
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final String key = preference.getKey();
if (key.equals(getString(R.string.key_scanning_delay))) {
final SeekBarPreference sbp = (SeekBarPreference) preference;
final int increment = sbp.getSeekBarIncrement();
float value = (int) newValue;
final int rounded = Math.round(value / increment);
final int finalValue = rounded * increment;
if (finalValue == value) return true;
else sbp.setValue(finalValue);
return false;
}
return true;
}
I'm using androidx.preference.SeekBarPreference, but there is probably little to no differences between these two libraries.
So, first check that it's the right preference. Then do the math. If the new calculated value is the same as the value the method was called with, return true so the value will be persisted. Most likely the values will differ on the first go. In that case, call the preference's setValue method again to update the view (and the value label if used). This time no math is needed (obviously) so it will return true and the value will be persisted. Finally return false so the original uncorrected value will be discarded.

In the documentation, the only xml attribute mentioned is android:thumb so what you're trying to do doesn't seem possible.
https://developer.android.com/reference/android/widget/SeekBar
I'd suggest going with either a programmatic approach or you could implement your own ViewGroup which accepts a parameter like seekBarIncrement and then passes it to the SeekBarPreference.

Related

android nested custom control xml attributes

I'm building an android compound control and nesting it into another compound control. The nested control, ThirtySecondIncrement, is a simple incrementing control with a minus then text field then plus so you can raise or lower the increment. I've made this control more general for my app allowing for a simple counter or 30-second increments or 1-minute increments. Here is the xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="counter_simple">0</integer>
<integer name="counter_30sec">1</integer>
<integer name="counter_1min">2</integer>
<declare-styleable name="ThirtySecondIncrement">
<attr name="countertype" format="integer"/>
<attr name="maxcount" format="integer"/>
</declare-styleable>
<declare-styleable name="IntervalEdit">
<attr name="label" format="string"/>
<attr name="incrementlabel" format="string"/>
</declare-styleable>
</resources>
My outer control includes labels and the ThirtySecondIncrement control. I would like to make the outer control flexible enough that I could include the "countertype" style to the outer control.
Can I do this in xml or must I do it programmatically? And if I do it programmatically how can I guarantee that it is done before the control is first used. Here is the code to extract the xml attributes:
public class ThirtySecondIncrement extends LinearLayout {
final int COUNT_INTEGER = 0;
final int COUNT_30SEC = 1;
final int COUNT_1MIN = 2;
//other code
public ThirtySecondIncrement(Context context, AttributeSet attr) {
super(context, attr);
TypedArray array = context.obtainStyledAttributes(attr, R.styleable.ThirtySecondIncrement, 0, 0);
m_countertype = array.getInt(R.styleable.ThirtySecondIncrement_countertype, COUNT_30SEC);
m_max = array.getInt(R.styleable.ThirtySecondIncrement_maxcount, MAXCOUNT);
m_increment = (m_countertype == COUNT_1MIN) ? 2 : 1;
array.recycle();
Initialize(context);
}
In a similar function in my IntervalEdit I could get an attribute relating to the counter and use a public function in ThirtySecondIncrement to set the countertype but, as stated, I'm wondering if there's a way to do this in xml.
thanks, in advance
I waited a while but got no answer so I solved the problem by having a helper function in the nested control to enable to set the property at runtime.
public void SetCounterType(integer countertype) {
//after checking that countertype is a valid value
m_countertype = countertype;
}
then in the constructor for IntervalEdit:
public IntervalEdit(Context context, AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.IntervalEdit, 0, 0);
//other attributes read here
m_counterstyle = array.getInt(R.styleable.IntervalEdit_counterstyle, R.integer.counter_simple);
(ThirtySecondIncrement)findViewById(R.id.tsi).SetCounterStyle(m_counterstyle);
The SetCounterStyle function in the first custom control only sets the variable and doesn't force a redraw enabling me to call it from within the including custom control's constructor.
Any, that's how I solved it.

Android custom image view using built-in attributes

It's my very first question here, so please go easy on me ;)
I've built my custom View class extending ImageView.
public class CustomImageView extends ImageView {
// ...
}
I have created a set of custom parameters for it in the shape of a <declare-styleable> item in the attrs.xml file.
<declare-styleable name="CustomImageView">
<attr name="angle" format="integer"/>
</declare-styleable>
I've figured out how to access (i.e. read from within the class and set from within the layout) these values.
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomImageView, 0, 0);
try {
a.getInt(R.styleable.CustomImageView_angle, 0);
} finally {
a.recycle();
}
So far, so easy. All of the above are directly taken from the guide.
However, I could not figure out how to access the inherited attributes of the ImageView class. Specifically, I want to read what was set as the src attribute of the ImageView. I'm assuming I have to use a different value for the second parameter of the obtainStyledAttributes(...) call, but I don't know what to use there and this obviously does not work:
a = context.getTheme().obtainStyledAttributes(attrs, ImageView, 0, 0);
So, how do I access the built-in attributes of my super class?
How do I get the int value (drawable res id) that was set for the android:src attribute?
Thanks for your help!
How do I get the int value (drawable res id) that was set for the
android:src attribute?
Use getAttributeResourceValue to get id of drawable :
public CustomImageView(Context context, AttributeSet attrs) {
super(context, attrs);
String android_schemas = "http://schemas.android.com/apk/res/android";
int srcId = attrs.getAttributeResourceValue(android_schemas, "src", -1);
}

SwipeListView in ExpandableListView, is it possible?

I am using https://github.com/47deg/android-swipelistview to create a list view with swipeable items. I am wondering is it possible to apply this to ExpandableListView so that child elements can be swiped.
I've been able to make it work (at least for me). Here's a list of the steps that I took:
Copy SwipeListView class and rename to ExpandableSwipeListView.
Make it extend from ExpandableListView.
Copy SwipeListViewTouchListener class and rename to ExpandableSwipeListViewTouchListener
Change SwipeListViewTouchListener calls inside ExpandableSwipeListView to ExpandableSwipeListViewTouchListener.
Change SwipeListView calls inside SwipeListViewTouchListener to ExpandableSwipeListView.
Change method resetItems of ExpandableSwipeListViewTouchListener to this:
-
/**
* Adds new items when adapter is modified
*/
public void resetItems() {
ExpandableListAdapter adp=swipeListView.getExpandableListAdapter();
if (adp != null) {
int count = 0;
for (int i=0; i<adp.getGroupCount();i++){
//Add the total children and the group itself.
count+=adp.getChildrenCount(i) + 1;
}
for (int i = opened.size(); i <= count; i++) {
opened.add(false);
openedRight.add(false);
checked.add(false);
}
}
}
Create expandableswipelistview__attrs.xml on res/values with teh following:
<declare-styleable name="ExpandableSwipeListView">
<attr name="swipeOpenOnLongPress"/>
<attr name="swipeAnimationTime"/>
<attr name="swipeOffsetLeft"/>
<attr name="swipeOffsetRight"/>
<attr name="swipeCloseAllItemsWhenMoveList"/>
<attr name="swipeFrontView"/>
<attr name="swipeBackView"/>
<attr name="swipeGroupView" format="reference"/>
<attr name="swipeMode"/>
<attr name="swipeActionLeft"/>
<attr name="swipeActionRight"/>
<attr name="swipeDrawableChecked"/>
<attr name="swipeDrawableUnchecked"/>
</declare-styleable>
Add tag swipe:swipeGroupView to the declaration of your ExpandableSwipeListView on the layout. Example:
-
swipe:swipeGroupView="#+id/group"
The id should be something unique and that must be declared on the layout of your groups.
On init method of ExpandableSwipeListView change all styleables to "ExpandableSwipeListView_..." and on "obtainStyledAttributes" set it to R.styleable.ExpandableSwipeListView.
Add swipeGroupView like swipeFrontView on the init method and pass it to ExpandableSwipeListViewTouchListener constructor.
On ExpandableSwipeListViewTouchListener add the following code after "if (allowSwipe && rect.contains(x, y)) {":
-
//verify if it is a group:
if (child.findViewById(swipeGroupView)!=null){
return false;
}
Add these methods to ExpandableSwipeListView. They are helpful to make dismiss callbacks and other things:
-
/**
* Returns the group and child positions for a child element.
* This values are passed inside an array of dimension 2 where the index 0 is the group position and the index 1 is the child position.
* #param general_position used on the list (compatible with getChildAt)
* #return int[2] 0 => group position; 1 => child position
*/
public int[] getGroupAndChildPositions(int general_position){
//get group and child ids
int groupPosition=0;
int childPosition=0;
int helper=general_position;
for (int i=0;i<getExpandableListAdapter().getGroupCount();i++){
if (helper-getExpandableListAdapter().getChildrenCount(i)-1<=0){
groupPosition=i;
childPosition=helper-1;
break;
} else {
helper-=getExpandableListAdapter().getChildrenCount(i)+1;
}
}
return new int[]{groupPosition,childPosition};
}
/**
* Returns the general position of an element on the list (used by getChildAt)
* #param groupPosition
* #param childPosition
* #return the position on the list
*/
public int getGeneralPosition(int groupPosition, int childPosition){
int position=0;
for (int i=0;i<=groupPosition;i++){
if (i<groupPosition)
position+=getExpandableListAdapter().getChildrenCount(i)+1;
else{
position+=childPosition+1;
}
}
return position;
}

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