Why is the onClickListener not getting called in my extended LinearLayout? When i include Setting_1 in my activity's layout, the onClick does not trigger.
Here is the layout of the compound control :
setting.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="wrap_content"
android:orientation="vertical"
>
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp" />
<TextView
android:id="#+id/status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp" />
</LinearLayout>
Here is the abstract class that uses the above layout :
IntegerSettingLayout.java
public abstract class IntegerSettingLayout extends LinearLayout implements
OnClickListener {
public IntegerSettingLayout(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.setting, this, true);
}
...
#Override
//this is not getting called
public void onClick(View view) {
if(BuildConfig.DEBUG) {Log.d(LOG_TAG,"onClick");}
}
abstract String getStatus();
...
}
Here is the class that implements the above abstract class
Setting_1.java
public class setting_1 extends IntegerSettingLayout{
public Setting_WeekFrequency(Context context, AttributeSet attrs) {
super(context, attrs);
...
}
#Override
String getStatus() {
if(BuildConfig.DEBUG) {Log.d(LOG_TAG,"getStatus");}
}
}
You haven't actually registered your OnClickListener. You need to call setOnClickListener() for that, or use android:onClick attribute in XML.
Add this.setClickable(true); to your constructor.
Related
I want to make widget which includes EditText and TextView and looks like this:
.
To achieve this I've created RelativeLayout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="48dp">
<EditText
android:id="#+id/editTextHint"
style="#style/CustomEditTextTheme"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="14dp"
android:textColor="#color/textColor"
android:textSize="16sp"
android:hint="To my first character"
tools:text="Тип клиента">
</EditText>
<TextView
android:id="#+id/textViewContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/editTextHint"
android:layout_alignParentEnd="true"
android:layout_above="#id/editTextHint"
tools:text="Продавец"
android:fontFamily="#font/proxima_nova_regular"
android:textSize="16sp"
android:layout_marginEnd="4dp"
android:textColor="#color/colorPrimary"/>
</RelativeLayout>
The above RelativeLayout works great and it looks as planned. To make Compound view I've removed </RelativeLayout> tag and wrapped it into the </merge> (I don't apply code as it's identical except of RelativeLayout tag)
To work with view I've written MyCustomEditText class which extendes RelativeLayout
public class CustomEditText extends RelativeLayout {
private EditText editTextHint;
private TextView textViewEntry;
private String hintText;
private String inputText;
private String starterText;
private OnCustomEditTextListener listener;
public String getHintText() {
return hintText;
}
public String getInputText() {
return inputText;
}
public void setHintText(String hintText) {
editTextHint.setText(hintText);
}
public void setInputText(String inputText) {
textViewEntry.setText(inputText);
}
public CustomEditText(Context context) {
super(context);
initializeViews(context);
}
public CustomEditText(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray;
typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomEditText);
hintText = typedArray.getString(R.styleable.CustomEditText_hintText);
inputText = typedArray.getString(R.styleable.CustomEditText_inputText);
starterText = typedArray.getString(R.styleable.CustomEditText_starterText);
typedArray.recycle();
initializeViews(context);
}
private void initializeViews(Context context) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.custom_edit_text, this);
setMinimumHeight(48);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
editTextHint = findViewById(R.id.editTextHint);
textViewEntry = findViewById(R.id.textViewContent);
bindDataToChildViews();
editTextHint.setOnClickListener(v -> listener.onClick());
}
private void bindDataToChildViews() {
editTextHint.setText(hintText);
if ((inputText == null)||(inputText.isEmpty())) {
textViewEntry.setText(starterText);
} else {
textViewEntry.setText(inputText);
}
}
public void setHintClickListener(OnCustomEditTextListener listener) {
this.listener = listener;
}
public interface OnCustomEditTextListener {
void onClick();
}
}
I'm trying tu use this view in other layouts but the positioning of text view is ugly:
I thought that extending view from RelativeLayout would be enough for correct positioning. So I need your help to define where I'me getting wrong. How to position elements correctly?
P.S. The replacement of RelativeLayout for merge tag was made to optimize drawing of views and avoiding unnecessary nesting layouts
First of all you dont need to have
android:layout_above="#id/editTextHint"
And add
android:layout_gravity="center_vertical"
to editTextHint and textViewContent
Also possibly you should remove
android:layout_marginEnd="4dp"
or change a bit it's value
In the image above, I have shown that when the user touches the drop-down spinner it will call the web api for getting data for the spinner. Then, that moment, I want to show the loader only on the spinner view on the left or right somewhere on the view itself like in the image, rather than on whole screen when it is getting data from the web service dynamically and hide that progress bar later when web service completely hit at the end (Ignore that search bar in image).
Just create an custom adapter for your spinner. Follow the instructions found here How to create Spinner-list using CustomAdapter in android .
Put the loading view in the layout inflated in the getView method in the adapter, and manipulate it via a callback from your async task used for fetching the result.
In this i am showing loader on start button and hiding loader when stop button is pressed so you can use according to your need.So , for this i have made three class CustomSpinner,Spinner_Custom_adapter and Activity class for using it
In main layout file
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical">
<www.your_packagename.com.spinnerwithloaderex.CustomSpinner
android:id="#+id/custm_spnr"
android:layout_width="120dp"
android:layout_height="40dp"
android:layout_centerInParent="true"
android:layout_gravity="center"
android:background="#drawable/dropdown_create_sales"
android:paddingRight="15dp"
android:text="Hello World!" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp">
<Button
android:id="#+id/start_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start" />
<Button
android:id="#+id/stop_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Stop" />
</LinearLayout>
</LinearLayout>
CustomSpinner class
public class CustomSpinner extends android.support.v7.widget.AppCompatSpinner {
private Spinner_Custom_adapter spinner_custom_adapter;
public CustomSpinner(Context context) {
super(context);
}
public CustomSpinner(Context context, int mode) {
super(context, mode);
}
public CustomSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode) {
super(context, attrs, defStyleAttr, mode);
}
public CustomSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode, Resources.Theme popupTheme) {
super(context, attrs, defStyleAttr, mode, popupTheme);
}
public void setItems(Activity activity, ArrayList<String> spnr_Arr) {
spinner_custom_adapter = new Spinner_Custom_adapter(activity, spnr_Arr);
setAdapter(spinner_custom_adapter);
}
public Spinner_Custom_adapter getSpinner_custom_adapter() {
return spinner_custom_adapter;
}
public void showLoader() {
setEnabled(false);
spinner_custom_adapter.showLoader(true, true);
}
public void dismissLoader() {
setEnabled(true);
spinner_custom_adapter.showLoader(false, true);
}
}
Custom_Adapter class
public class Spinner_Custom_adapter<T> extends ArrayAdapter<T> {
private LayoutInflater flater;
private ProgressBar spinner_progress;
private TextView txtTitle;
private Boolean showOrNot = false;
Spinner_Custom_adapter(Activity context, ArrayList<T> list) {
super(context, R.layout.loader_spinner_lt, R.id.title, list);
flater = context.getLayoutInflater();
}
#NonNull
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = flater.inflate(R.layout.loader_spinner_lt, parent, false);
}
Object object = getItem(position);
String rowItem = null;
if (object instanceof String) {
rowItem = (String) object;
}
TextView txtTitle = (TextView) convertView.findViewById(R.id.title);
txtTitle.setText(rowItem);
ProgressBar spinner_progress = (ProgressBar) convertView.findViewById(R.id.spinner_progress);
this.txtTitle = txtTitle;
this.spinner_progress = spinner_progress;
showLoader(showOrNot, false);
return convertView;
}
void showLoader(Boolean showOrNot, boolean notifyListOrNot) {
if (txtTitle != null && spinner_progress != null) {
this.showOrNot = showOrNot;
spinner_progress.setVisibility(showOrNot ? View.VISIBLE : View.GONE);
if (notifyListOrNot) {
notifyDataSetChanged();
}
}
}
}
Spinner single view layout xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:orientation="horizontal">
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/title"
style="?android:attr/spinnerDropDownItemStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="#+id/spinner_progress"
android:ellipsize="end"
android:singleLine="true"
android:text="Strawberry"
android:textColor="#CC0033"
android:textSize="16dp" />
<ProgressBar
android:id="#+id/spinner_progress"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:visibility="gone" />
</RelativeLayout>
and for using it
custm_spnr = (CustomSpinner) findViewById(R.id.custm_spnr);
ArrayList<String> items = new ArrayList<>();
items.add("Abcdefg");
items.add("hijklm");
items.add("nopqr");
items.add("stu");
items.add("vwxyza1b1c1");
items.add("d1e1f11g1h1");
custm_spnr.setItems(MainActivity.this, items);
findViewById(R.id.start_btn).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
custm_spnr.showLoader();
}
});
findViewById(R.id.stop_btn).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
custm_spnr.dismissLoader();
}
});
I want to have two custom views like this:
class LoadingView extends RelativeLayout{
//this view has a progressBar and can show and hide it.
void setIsLoading(){
// shows/hides progress bar
}
// some code ...
}
class OtherView extends LoadingView{
//some code ...
}
LoadingView has a layout like this:
<RelativeLayout>
<FrameLayout
id="#+id/content">
<!--this is where content goes-->
</FrameLayout>
<ProgressBar
id="#+id/pb"/>
</RelativeLayout>
so that any custom view that inherits from it will be injected into FrameLayout
so if OtherView has it's own layout , it will be nested inside FrameLayout automatically and you will be able to call myOtherView.setIsLoading(true/false)
How would you suggest doing this ?
Keep a reference to the content FrameLayout when inflating the first view.
public class LoadingView extends RelativeLayout {
private ProgressBar progressBar;
private FrameLayout contentFrame;
public LoadingView(Context context) {
super(context);
initialize();
}
public LoadingView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public LoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize();
}
private void initialize() {
View root = inflate(getContext(), R.layout.loading_view, this);
progressBar = (ProgressBar) root.findViewById(R.id.progress_bar);
contentFrame = (FrameLayout) root.findViewById(R.id.content_frame);
}
public void setLoading(boolean loading) {
progressBar.setVisibility(loading ? VISIBLE : GONE);
}
protected FrameLayout getContentFrame() {
return contentFrame;
}
}
Then use getContentFrame as the parent view when inflating the child view.
public class OtherView extends LoadingView {
public OtherView(Context context) {
super(context);
initialize();
}
public OtherView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public OtherView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize();
}
private void initialize() {
View root = inflate(getContext(), R.layout.other_view, getContentFrame());
}
}
loading_view.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ProgressBar
android:id="#+id/progress_bar"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
other_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical">
<TextView
android:text="Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:text="Subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
And use it like this
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
<com.example.client.ui.OtherView
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
package com.binod.customviewtest;
import android.content.Context;
import android.view.LayoutInflater;
import android.widget.RelativeLayout;
public class CustomView extends RelativeLayout{
public CustomView(Context context) {
super(context);
// TODO Auto-generated constructor stub
// LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LayoutInflater mInflater = LayoutInflater.from(context);
mInflater.inflate(R.layout.custom_view , this, true);
}
}
Including as
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<com.binod.customviewtest.CustomView
android:layout_width="match_parent"
android:layout_height="wrap_content"
></com.binod.customviewtest.CustomView>
</RelativeLayout>
Custom View as
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world" />
</RelativeLayout>
Just started adding a new custom view and got the error once If I clear this then can move forward
I am getting crash "Caused by: android.view.InflateException: Binary XML file line #1: Error inflating class"
You need to have 2 more constructors. To know why
Do I need all three constructors for an Android custom view?
public class CustomView extends RelativeLayout{
LayoutInflater mInflater;
public CustomView(Context context) {
super(context);
mInflater = LayoutInflater.from(context);
init();
}
public CustomView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
mInflater = LayoutInflater.from(context);
init();
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
mInflater = LayoutInflater.from(context);
init();
}
public void init()
{
View v = mInflater.inflate(R.layout.custom_view, this, true);
TextView tv = (TextView) v.findViewById(R.id.textView1);
tv.setText(" Custom RelativeLayout");
}
}
I am posting an example. My packagename is different
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<com.example.testall.CustomView
android:id="#+id/timer1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</RelativeLayout>
custom_view.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="60dp"
android:text="My Custom View" />
</RelativeLayout>
MainActivity.java
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Snap
As pskink suggested there in a RelativeLayout in activity_main.xml with a child CustomView. Then CustomView extends RealtiveLayout and then again you inflate a customview with RelativeLayout and a child TextView. No need for all these. Just a CustomView. Have a TextView created programatically and then add textview to RelativeLayout
Edit:
activity_main.xml
<com.example.testall.CustomView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/timer1"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
CustomView
public class CustomView extends RelativeLayout{
TextView tv;
public CustomView(Context context) {
super(context);
tv = new TextView(context);
init();
}
public CustomView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
tv = new TextView(context);
init();
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
tv = new TextView(context);
init();
}
public void init()
{
this.addView(tv);
tv.setText(" Custom RelativeLayout");
}
}
Try to get Activity and use this
{
LayoutInflater inflter = activity.getLayoutInflater();
View v = inflter.inflate(R.layout.custom_view,null);
this.addView(v); or addView(v);
}
From the fragment layout Demo:
there is a simple_list_item_checkable_1.xml
<com.example.android.supportv4.view.CheckableFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"
/>
</com.example.android.supportv4.view.CheckableFrameLayout>
and there is a class CheckableFrameLayout.java
public class CheckableFrameLayout extends FrameLayout implements Checkable {
private boolean mChecked;
public CheckableFrameLayout(Context context) {
super(context);
}
public CheckableFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setChecked(boolean checked) {
mChecked = checked;
setBackgroundDrawable(checked ? new ColorDrawable(0xff0000a0) : null);
}
public boolean isChecked() {
return mChecked;
}
public void toggle() {
setChecked(!mChecked);
}
}
I really don't know why the demo write in such way and can I switch it to XML code?
I have tried to Google but I still cannot find any answer.
CheckableFrameLayout is a custom display element, not a built-in Android UI widget. Its behavior is defined in the source code, and its UI is defined in the XML.