Here is a random layout called some_layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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"
android:layout_width="match_parent"
android:layout_height="match_parent"">
<!-- Some views in here -->
</androidx.constraintlayout.widget.ConstraintLayout>
Here is a class SomeDialog.java that extends DialogPreference:
import android.content.Context;
import androidx.preference.DialogPreference;
public class SomeDialog extends DialogPreference {
public SomeDialog(Context context) {
super(context);
}
#Override
public int getDialogLayoutResource() {
return R.layout.some_layout;
}
}
And here's the preference screen:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<android.example.perappbrightness.SomeDialog
android:title="#string/rate_app"
android:summary="#string/rate_summary"
android:key="rate_app"/>
</PreferenceScreen>
Relevant part of crash error. The line at which MainActivty crashes is PreferenceManager.setDefaultValues(this, R.xml.preferences, false);:
java.lang.RuntimeException: Unable to start activity ComponentInfo{android.example.perappbrightness/android.example.perappbrightness.MainActivity}: android.view.InflateException: Binary XML file line #36: Error inflating class android.example.perappbrightness.SomeDialog
Caused by: android.view.InflateException: Binary XML file line #36: Error inflating class android.example.perappbrightness.SomeDialog
android.example.perappbrightness.MainActivity.onCreate(MainActivity.java:72)
SettingsActivity.java is an untouched. It's from the template of Android Studio.
What am I doing wrong?
First, you need to add more constructors to SomeDialog. The Context constructor is not enough since the Preference will be inflated from xml. Having the following three constructors is usually sufficient:
public SomeDialog(Context context) {
super(context);
}
public SomeDialog(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SomeDialog(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
In addition to that, the SettingsFragment in your SettingsActivity needs to implement onDisplayPreferenceDialog(Preference preference) to show a custom dialog for the custom Preference.
public static class SettingsFragment extends PreferenceFragmentCompat {
#Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.root_preferences, rootKey);
}
#Override
public void onDisplayPreferenceDialog(Preference preference) {
if (preference instanceof SomeDialog) {
MyDialogFragment dialogFragment = new MyDialogFragment();
Bundle b = new Bundle();
b.putString(MyDialogFragment.KEY, preference.getKey());
b.putInt(MyDialogFragment.KEY_LAYOUT_RES_ID, ((SomeDialog) preference).getDialogLayoutResource());
dialogFragment.setArguments(b);
dialogFragment.setTargetFragment(this, 0);
dialogFragment.show(getFragmentManager(), null);
} else super.onDisplayPreferenceDialog(preference);
}
}
And, last not least, you also have to provide the custom dialog itself. This is done via a class extending DialogFragment.
My very simple DialogFragment has a TextView inside a FrameLayout, just to show it works
MyDialogFragment code:
public class MyDialogFragment extends DialogFragment {
public static final String KEY = "key";
public static final String KEY_LAYOUT_RES_ID = "resid";
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(requireArguments().getInt(KEY_LAYOUT_RES_ID), container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
TextView textView = view.findViewById(R.id.textView);
textView.setText(requireArguments().getString(KEY));
}
}
Related
I've created a custom ImageView class, and I'm trying to change the imageResource when user click on it, but I'm able to call setImageResource() from that class.
Also I'd like to store like a second imageView I mean, my custom ImageView has the same starter imageView resource, but when click on it it have to be dynamic ImageView for instance:
ImageView1 ic_launcher (user has not clicked on it)
ImageView1 ic_user (user has clicked on it)
Can you guide how to achieve this?
This is my custom ImageView class :
public class CustomImageView extends android.support.v7.widget.AppCompatImageView implements View.OnClickListener {
private View.OnClickListener clickListener;
public CustomImageView(Context context) {
super(context);
setOnClickListener(this);
}
public CustomImageView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
setOnClickListener(this);
}
public CustomImageView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setOnClickListener(this);
}
#Override
public void setOnClickListener(OnClickListener l) {
if (l == this) {
super.setOnClickListener(l);
} else {
clickListener = l;
}
}
#Override
public void onClick(View v) {
//Should change the imageResource here but also I should have to change it again if user wants (to the initial one)
if (clickListener != null) {
clickListener.onClick(this);
}
}
}
A solution for a custom toggleable ImageView:
Custom attribute in values/attr.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ToggleImageView">
<attr name="low_img" format="reference" />
<attr name="high_img" format="reference" />
</declare-styleable>
</resources>
Custom ImageView class:
public class ToggleImageView extends AppCompatImageView implements View.OnClickListener {
private Drawable mLowDrawable, mHighDrawable;
private boolean isLow = true;
public ToggleImageView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
// Extract drawables from custom attributes
TypedArray values = context.obtainStyledAttributes(attrs, R.styleable.ToggleImageView);
setLowDrawable(values.getDrawable(R.styleable.ToggleImageView_low_img));
setHighDrawable(values.getDrawable(R.styleable.ToggleImageView_high_img));
values.recycle();
setImageDrawable(mLowDrawable);
super.setOnClickListener(this);
}
public void setLowDrawable(Drawable drawable) {
mLowDrawable = drawable;
if (isLow)
setImageDrawable(mLowDrawable);
}
public void setHighDrawable(Drawable drawable) {
mHighDrawable = drawable;
if (!isLow)
setImageDrawable(mHighDrawable);
}
#Override
public void setOnClickListener(#Nullable OnClickListener l) {
// Do nothing to block setting listener from outer caller
}
#Override
public void onClick(View view) {
toggle();
}
public void toggle() {
isLow = !isLow;
setImageDrawable(isLow ? mLowDrawable : mHighDrawable);
}
}
Usage in xml layout:
<?xml version="1.0" encoding="utf-8" ?>
<FrameLayout 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"
android:id="#+id/root_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.tamhuynh.testfragment.ToggleImageView
android:id="#+id/toggle_img"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
app:high_img="#mipmap/high_drawable"
app:low_img="#drawable/low_drawable"
tools:low_img="#drawable/low_drawable" />
</FrameLayout>
I want to rewrite my activities to dialogs, and I want to replace dialogs programmatically. I tried fragments staticaly and it is works fine. When I programmatically call fragment and debug it with breakpoints I don't understand why its lifecycle starts from onCreate() and ends on it... Why from onCreate() and don't from onAttach() like when I call it statically? here is my code, help me plz!!!
base_activity.xml `
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/base_drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/base_fragment_holder"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
</android.support.v4.widget.DrawerLayout>
`
BaseActivity.java
public class BaseActivity extends AppCompatActivity {
#Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.base_activity);
DietsFragment dietsFragment = new DietsFragment();
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.base_fragment_holder, dietsFragment);
fragmentTransaction.commit();
}
}
DietsFragment.java
public class DietsFragment extends Fragment
implements AdapterView.OnItemClickListener,
View.OnClickListener {
public FragmentEventListener fragmentEventListener;
Cursor diets;
DietAdapter adapter;
DietHelper helper;
ListView listView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View view = inflater.inflate(R.layout.activity_diets, container, false);
listView = (ListView) view.findViewById(R.id.act_diets_lv_diets);
if (listView != null) {
listView.setOnItemClickListener(this);
}
FloatingActionButton floatingActionButton = (FloatingActionButton) view.findViewById(R.id.act_diets_fab);
if (floatingActionButton != null)
floatingActionButton.setOnClickListener(this);
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
setHelper(new DietHelper(getActivity()));
setDiets(getHelper().getAllTableRecordsCursor());
setAdapter(new DietAdapter(getActivity(), getDiets()));
listView.setAdapter(getAdapter());
fragmentEventListener = (FragmentEventListener) getActivity();
}
public void setDiets (Cursor diets) {
this.diets = diets;
}
public void setAdapter (DietAdapter adapter) {
this.adapter = adapter;
}
public void setHelper (DietHelper helper) {
this.helper = helper;
}
public Cursor getDiets() {
return diets;
}
public DietAdapter getAdapter() {
return adapter;
}
public DietHelper getHelper() {
return helper;
}
}
in DietsFragment and BaseActivity i hide its implementetions of OnClickListener, OnItemClickListener and FragmentEventListener, cause it works fine when i add fragment in BaseActivity statically from xml with
I've just found my mistake... I overloaded findViewbyId(int resId) in BaseActivity and that all my problem, and now i'm asking myself WHY I OVERLOAD THAT METHOD WHAT WAS I WANTED TO DO WITH IT?
I have a following Fragment class:
public class TabFragment extends Fragment {
....
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_photos_tab, container, false);
TabelAdapter adapt = new TabelAdapter(getActivity().getApplicationContext(), R.layout.list_room_view, flowerList);
setListAdapter(adapt); // Not Working because class is not extended by ListActivity
return view;
}
TabelAdapter class
public class TabelAdapter extends ArrayAdapter<Flower> {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
if (null == convertView) {
convertView = inflater.inflate(R.layout.list_flower_view, parent, false);
}
Picasso
.with(context)
.load("http://i.imgur.com/rFLNqWI.jpg")
.fit()
.into((ImageView) convertView);
return convertView;
}
list_flower_view.xml
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/Flowerimg"
android:layout_width="match_parent"
android:layout_height="200dp"/>
activity_home.xml
<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">
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#android:id/list"></ListView>
</RelativeLayout>
When I was working with activity instead of fragment then the whole process was very simple. I extend MainActivity class with ListActivity and add setContentView(R.layout.activity_home); into MainActivity.OnCreate(). I can see the list of Images in ListView.
Question
How to make setListAdapter usable in Fragment class? I am newbie in Android. Any help will be appreciable :)
Edit-1
public class PagerAdapter extends FragmentStatePagerAdapter {
int mNumOfTabs;
public PagerAdapter(FragmentManager fm, int NumOfTabs) {
super(fm);
this.mNumOfTabs = NumOfTabs;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
TabFragment photo = new TabFragment();
return photo; // Line (30,24)
} ......
}
If I use ListFragment instead of Fragment then PagerAdapter class os throwing an Error.
Error:(30, 24) error: incompatible types: TabFragment cannot be converted to Fragment
Edit-2
java.lang.RuntimeException: Your content must have a ListView whose id attribute is 'android.R.id.list'
Change Fragment to ListFragment:
public class TabFragment extends ListFragment {
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
TabelAdapter adapt = new TabelAdapter(getActivity().getApplicationContext(), R.layout.list_room_view, flowerList);
setListAdapter(adapt);
}
...
}
ListFragment documentation
Tutorial
I have extended BaseActivity from ActionBarActivity, in which I set the activity's content. There's a FrameLayout in the layout file I use.
When I extend BaseActivity to use in e.g. MainActivity, I'd like MainActivity to inflate the FrameLayout with a custom layout file.
I couldn't come up with a solution. I always got errors. This is how far I came.
BaseActivity.java
public class BaseActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.base);
}
}
MainActivity.java
public class MainActivity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
FrameLayout mFrame = (FrameLayout) findViewById(R.id.content);
mFrame.addView(LayoutInflater.from(context).inflate(R.layout.activity_nav_test, mFrame, true));
return super.onCreateView(parent, name, context, attrs);
}
Thanks a lot for your help!
Chris
Your approach seems to be proper but add view inside frame layout in onCreate method instead.
public class MainActivity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FrameLayout mFrame = (FrameLayout) findViewById(R.id.content);
mFrame.addView(LayoutInflater.from(this).inflate(R.layout.activity_nav_test, mFrame, true));
}
In this method :
#Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
FrameLayout mFrame = (FrameLayout) findViewById(R.id.content);
mFrame.addView(LayoutInflater.from(context).inflate(R.layout.activity_nav_test, mFrame, true));
return super.onCreateView(parent, name, context, attrs);
}
You must return ur custom view. Try this. I hope it helps
return mFrame;
I crated a datetime component but it's constructed automatically (I have it in a XML layout and I don't want to create it manually) but I need to pass a reference to an Activity in order to create dialogs. How can I achieve that? I tried a setter after findViewById but it's not a good solution...
public class DateTimeComponent extends RelativeLayout {
private Activity activity;
public DateComponent(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
// rest ommited
initFields();
}
private void initFields() {
dateEditText = (EditText) findViewById(R.id.dateEditText);
dateEditText.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
activity.showDialog(DATE_PICKER_DIALOG);
}
});
timeEditText = (EditText) findViewById(R.id.timeEditText);
timeEditText.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
activity.showDialog(TIME_PICKER_DIALOG);
}
});
}
// rest ommited
public Dialog getDatePickerDialog() {
int year = selectedDateTime.get(YEAR);
int month = selectedDateTime.get(MONTH);
int day = selectedDateTime.get(DAY_OF_MONTH);
return new DatePickerDialog(activity, onDateSetListener, year, month, day);
}
public Dialog getTimePickerDialog() {
int hour = selectedDateTime.get(HOUR_OF_DAY);
int minute = selectedDateTime.get(MINUTE);
return new TimePickerDialog(activity, onTimeSetListener, hour, minute, true);
}
private final OnDateSetListener onDateSetListener = new OnDateSetListener() {
#Override
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
// do something
}
};
private final OnTimeSetListener onTimeSetListener = new OnTimeSetListener() {
#Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
// do something
}
};
}
Perhaps this may help you:
Option 1:
public class DateTimeComponent extends RelativeLayout {
private Activity activity;
public DateTimeComponent(Activity act){
activity = act;
}
public void someListener() {
activity.showDialog(...);
}
}
Option 2:
public class DateTimeComponent extends RelativeLayout {
public void someListener(Activity act) {
act.showDialog(...);
}
}
Option 3:
...
private Activity activity;
public DateComponent(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
activity = (Activity) getContext();
// rest ommited
initFields();
}
...
Two ways -
Create a constructor that accepts a Context parameter, and have a (private?) class variable of type Context which you can use whenever.
Add an extra Context context parameter for every method that will be needing it. In some cases you may need to make that final.
The context your constructor receives IS an Activity. So, you can cast it to it. For example like this
MyActivity a = (MyActivity) getContext();
P.S. You do not need to store activity in your own field:
private Activity activity; // not needed
it is already stored inside and can be obtained by http://developer.android.com/reference/android/view/View.html#getContext()
PROOF
Custom text view:
public class MyTextView extends TextView {
public MyTextView(Context context) {
super(context);
setText(Integer.toString(System.identityHashCode(context)));
}
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
setText(Integer.toString(System.identityHashCode(context)));
}
}
Activity:
public class ContextActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView textView = (TextView) findViewById(R.id.textView);
textView.setText( Integer.toString(System.identityHashCode(this)) );
}
}
Layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/textView" />
<com.inthemoon.incubation.MyTextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
The codes diplayed are identical.