Binding varargs using android databinding library - android

I am trying to write a custom setter for SwipeRefreshLayout's
setColorScheme(int... colors).
But it seems that its parameter is varargs.
I can only bind a single color now like the following:
#BindingAdapter("app:colorSchemeResources")
public static void bindRefreshColor(SwipeRefreshLayout swipeRefreshLayout, int colorResId) {
swipeRefreshLayout.setColorSchemeColors(colorResId);
}
xml:
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipe_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:colorSchemeResources ="#{#color/primary}"
/>
My question is:
How can I write a custom setter for varargs?
How to bind varargs in the xml file?

Please try this:
Specify an integer array containing the colors you want in your colors.xml for example:
<integer-array name="color_scheme" >
<item>#color/first_color</item>
<item>#color/second_color</item>
</integer-array>
Change your bindingadapter like this:
#BindingAdapter("app:colorSchemeResources")
public static void bindRefreshColor(SwipeRefreshLayout swipeRefreshLayout, int[] colorResIds) {
swipeRefreshLayout.setColorSchemeColors(colorResIds);
}
And reference your array in your view:
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipe_refresh_layout"
android:layout_width="match_parent"
app:colorSchemeResources ="#{#intArray/color_scheme}"
android:layout_height="wrap_content">

Related

how to specify class (not string) in android layout data binding

I have added a new binding rule like this for ImageView which takes a custom object:
#BindingAdapter({"custDrawable"})
public static void setCustDrawable(#NonNull ImageView view, HexDrawableModel model) {
view.setImageDrawable(new HexDrawable(model));
}
where HexDrawable extend Drawable, and
data class HexDrawable(val text: String, val color: Color)
So... I am not sure how to use this binding adapter in my layout file because it is expecting a class not string. Please let me know to use this binding adapter (if it is even possible).
In layout XML:
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
custDrawable=??? />
I think you should declare them like this
#BindingAdapter("custDrawable")
public static void setCustDrawable(#NonNull ImageView view, HexDrawableModel model) {
view.setImageDrawable(new HexDrawable(model));
}
And use them like this
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
...
</data>
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
app:custDrawable="#{yourVariableHere}/>
</layout>
use app:custDrawable={$variable}
define the variable in data section of the layout and bind it in respective java file

How to obtain the value for a reference attribute

I want to implement a SeekBar that automatically updates a TextView, for the actual value, the maximum value and a minimum value. I derive from SeekBar and define 3 named attributes with the format being reference.
In the constructor, I get a TypedArray by calling obtainStyledAttributes(). TypedArray contains a lot of getters for every kind of attribute types. What I am missing is some kind of Object getReference() or int getReferenceId().
How do I obtain the value for a reference attribute?
Edit:
I have an attribute definition for a class MinMaxSlider, that looks like this:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MinMaxSlider">
<attr name="min" format="integer" />
<attr name="valueView" format="reference" />
</declare-styleable>
</resources>
a snipped out of the layout definition looks like this:
<LinearLayout
style="#style/ParameterLabelLayout"
android:orientation="horizontal">
<TextView
style="#style/ParameterSliderLabel"
android:text="min. Interval" />
<TextView
android:id="#+id/min_connection_interval_slider_value"
style="#style/ParameterSliderValue"/>
</LinearLayout>
<com.roche.rcpclient.MinMaxSlider
style="#style/ParameterSlider"
android:id="#+id/min_connection_interval_slider"
android:max="3200"
custom:valueView="#id/min_connection_interval_slider_value"
custom:min="1"/>
Here, the MinMaxSlider should reference one of the TextViews above to display its current value there.
From within the constructor of MinMaxSlider, I can lookup the min attributes value.
If I try to lookup the valueView attribute as an integer, I always get the default value (0), not R.id.min_connection_interval_slider as I would expect.
Edit: the right function to lookup a reference seems to be getResourceId. The obtained integer id can be used to use findViewById later, when the overall object hierarchy is constructed.
In my case, I register an OnSeekBarChangeListener() and lookup the View in the callback, when the callback is fired.
You can't receive reference via findViewById in constructor. Because it is not attached to layout yet.
Reference: https://developer.android.com/training/custom-views/create-view.html#applyattr
When a view is created from an XML layout, all of the attributes in
the XML tag are read from the resource bundle and passed into the
view's constructor as an AttributeSet. Although it's possible to read
values from the AttributeSet directly, doing so has some
disadvantages:
Resource references within attribute values are not resolved Styles
are not applied Instead, pass the AttributeSet to
obtainStyledAttributes(). This method passes back a TypedArray array
of values that have already been dereferenced and styled.
You can receive in another methods.
For example:
attrs.xml
<declare-styleable name="CustomTextView">
<attr name="viewPart" format="reference"></attr>
</declare-styleable>
yourLayout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:gravity="center_vertical"
android:orientation="horizontal">
<tr.com.ui.utils.CustomTextView
android:id="#+id/chat_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|bottom"
android:focusableInTouchMode="false"
android:gravity="left"
android:text="test"
app:viewPart="#id/date_view" />
<TextView
android:id="#+id/date_view"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:gravity="right" />
</LinearLayout>
CustomTextView.java
public class CustomTextView extends TextView {
private View viewPart;
private int viewPartRef;
public CustomTextView(Context context) {
super(context);
}
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomTextView, 0, 0);
try {
viewPartRef = a.getResourceId(R.styleable.CustomTextView_viewPart, -1);
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
viewPart = ((View) this.getParent()).findViewById(viewPartRef);
}
public View getViewPart() {
return viewPart;
}
public void setViewPart(View viewPart) {
this.viewPart = viewPart;
}}
You can decide your scenario and modify this code.

Android Array Adaptor - insert a checkbox into each row

Right so, I have a navbar layout. The <FrameLayout> in content_main is replaced with stand1.xml when the stand1 button in my navbar is pressed. This works without any issue.
Content_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
app:layout_behavior="#string/appbar_scrolling_view_behavior" tools:showIn="#layout/app_bar_main"
tools:context=".MainActivity">
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"></FrameLayout>
</RelativeLayout>
stand1.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<ListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/Stand1list">
</ListView>
</LinearLayout>
from main activity
if (id == R.id.Stand1) {
fm.beginTransaction().replace(R.id.content_frame, new Stand1()).commit();
setTitle(R.string.Stand1);
Following tutorials online this is my Stand1.java file which is used to populate the a list with a array of strings
public class Stand1 extends Fragment {
ListView mList;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.stand1,container,false);
mList = (ListView) root.findViewById(R.id.Stand1list);
return root;
}
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
populateListView();
}
private void populateListView() {
String[] pair = {"Pair1","Pair2","Pair3","Pair4","Pair5"};
//build adapter
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), R.layout.row_layout,pair);
mList.setAdapter(adapter);
}
}
and row_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/text1"
xmlns:android="http://schemas.android.com/apk/res/android" />
This is working, It displays the 5 strings. However i want to make it so i can also have a check box in each row.
If i try place linear layout in row_layout.xml , i get error
ArrayAdapter requires the resource ID to be a TextView
I was following stackoverflow which sorted other errors which suggested that i had to make the list at class level and inflate it in onCreate.
I know i prob have to make an custom array adapter, but my problem is how do i deal with the inflation of the view in the on create.
Im not really sure how i go about doing this. So can somebody tell me from here, how do i go about having a check box in each row.
Thanks for the help
ArrayAdapter needs to know where to put the text from your string array. By default, it assumes the layout you pass it is a TextView and will set its text to one of the strings. If you pass it a more complicated layout, you need to tell it the id of the TextView you want it to use. You can do that with this constructor.

Set an XML file as a custom View?

I have a custom Table Row that I am in the process of making. I want to use an XML file to define what a single row looks like. I would like to have a class extend TableRow and define itself to be the file as defined in the XML. The XML file might look like:
<TableRow xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<TextView
android:id="#+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingRight="10dp"
android:text="#string/loading"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="#+id/data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right"
android:text="#string/loading"
android:textAppearance="?android:attr/textAppearanceLarge" />
</TableRow>
And the code might look like:
public class SpecialTableRow extends TableRow {
public SpecialTableRow (Context context) {
}
}
Is there something that I can put into the constructor to have the class assume it is the tableRow in it's entirety? Alternatively, is there another structure which would work better? The best that I've figured out is this:
TableRow tr=(TableRow) LayoutInflater.from(context).inflate(R.layout.text_pair,null);
TextView mFieldName=(TextView) tr.findViewById(R.id.label);
TextView mValue=(TextView) tr.findViewById(R.id.data);
tr.removeAllViewsInLayout();
addView(mFieldName);
addView(mValue);
But this removes the layout parameters from the XML. Anything better out there?
Take a look at the tutorial on creating custom views. You will want to subclass TableRow and add the additional views you want to display. Then, you can use your new view directly in your XML layouts and additionally create any custom attributes you might want. I've included an example which creates a custom TableRow named TextPairRow, inflates a layout with two TextViews to show within the TableRow and adds showLabel and showData custom attributes which show/hide the two TextViews. Finally, I've included how you would use your new view directly in your XML layouts.
class TextPairRow extends TableRow {
private TextView label, data;
public TextPairRow (Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.TextPairRow, 0, 0);
try {
showLabel = a.getBoolean(R.styleable.TextPairRow_showLabel, false);
showData = a.getBoolean(R.styleable.TextPairRow_showData, false);
} finally {
a.recycle();
}
initViews();
}
private void initViews(){
// Here you can inflate whatever you want to be in your
// view or add views programatically.
// In this example, we'll just assume you have a basic XML
// layout which defines a LinearLayout with two TextViews.
LinearLayout mLayout = (LinearLayout)
LayoutInflater.from(getContext()).inflate(R.layout.textview_layout, this);
label = (TextView) mLayout.findViewById(R.id.label);
data = (TextView) mLayout.findViewById(R.id.data);
if(showLabel)
label.setVisibility(View.VISIBLE);
else
label.setVisibility(View.GONE); // can also use View.INVISIBLE
// depending on your needs
if(showData){
data.setVisibility(View.VISIBLE);
else
data.setVisibility(View.GONE); // can also use View.INVISIBLE
// depending on your needs
}
}
This is where you define your custom XML attributes (locate or create this file: res/values/attrs.xml)
<resources>
<declare-styleable name="TextPairRow">
<attr name="showText" format="boolean" />
<attr name="showLabel" format="boolean" />
</declare-styleable>
</resources>
Finally, to use your new view directly in your XML layouts:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto">
<com.thefull.packageforyourview.TextPairRow
android:orientation="horizontal"
custom:showData="true"
custom:showLabel="true" />
</LinearLayout>
Note that you might need to use xmlns:custom="http://schemas.android.com/apk/res/com.thefull.packageforyourview" depending on if your custom view will be in a library project. Regardless, either this or what's in the example will work.
The real trick to doing this is actually quite simple. Use the second parameter of the inflate method. In fact, the best thing to do is this:
LayoutInflater.from(context).inflate(R.layout.text_pair,this);
This will inflate the R.layout.text_pair into this, effectively using the entire row. No need to add the view manually, Android takes care of it for you.
The only thing I can think of is to use a static method instead of constructor. For example:
public static void newInstance (Context context) {
this = context.getLayoutInflater().inflate(R.layout.text_pair, null, null);
}
Then don't use constructor for initializing an object, call this method.

Custom view updating content of another view

I have the following situation:
in the main file I do
setContentView(R.layout.main);
which looks as follows:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/layout"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/result"
android:text="Gesamtstrecke: 0.0"
/>
<test.gustav.Spielfeld
android:id="#+id/spielfeld"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
</test.gustav.Spielfeld>
</LinearLayout>
the View "test.gustav.Spielfeld" refers to a class "Spielfeld" extending "View".
I now need a method that updates the content of the TextView with id "result" if a something happens in the onTouchEvent(..)-method in the Spielfeld-View.
How to do this? I've tried making a public TextView in the Main-Class but the Spielfeld class won't accept Main.this.myTextView.
I would recommend a callback approach (just like android does for all events):
Create a interface which represents the callback:
public interface OnSpielfeldUpdate {
public void onSpielfeldUpdate(Object myEventData, ...);
}
Provide a setter for the callback in SpielFeld (which will be used to send updates):
public void setOnSpielfeldUpdate(OnSpielfeldUpdate callback) {
this.callback = callback;
}
Set your callback in your activity (or somewhere else):
mySpielfeld.setOnSpielfeldUpdate(new OnSpielfeldUpdate() {
public void onSpielfeldUpdate(Object myEventData, ...) {
// perform update on result view
}
}

Categories

Resources