I'd like to change the locale on my app (runtime).It works fine so far. The only thing left is refreshing the current activity.
The documentation says that updating the locale Configuration should restart the running activity (same behavior as the screen orientation) but it does restart it in my case. (I did not use the android:configChanges="locale" to prevent this behavior).
Here are the key parts :
I have my activity :
public class SettingsActivity extends Activity {
ExpandableListAdapter listAdapter;
//....
}
An Adapter where I bind a click (inside the getChildView function) :
public View getChildView(final int groupPosition, final int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
//...
convertView = inflater.inflate(R.layout.list_item, null);
convertView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
I18N.setLang(context, "fr");
};
});
}
And the setLang method inside my I18N class :
public class I18N {
public static void setLang(final Context context, String langCode)
{
if (langCode.equals(""))
return;
try {
final Locale newLocale;
final String[] languageRegion = langCode.split("\\-(r)?");
I18N.saveLocale(context, langCode);
newLocale = languageRegion.length > 1 ? new Locale(languageRegion[0], languageRegion[1]) : new Locale(languageRegion[0]) ;
current = newLocale;
Resources res = context.getResources();
DisplayMetrics dm = res.getDisplayMetrics();
android.content.res.Configuration conf = res.getConfiguration();
conf.locale = new Locale(languageRegion[0]);
res.updateConfiguration(conf, dm);
}
catch (Exception e) {
Log.e("exception", e.getMessage() + e.getClass());
}
}
}
I have tried this. It kind of works but I don't like this idea of refreshing the activity manually.
The main issue is that despite using the flag FLAG_ACTIVITY_NO_HISTORY, the history (using the back button of the mobile device) does not work properly (each click on the list to change the language still create an history) :
Intent refresh = new Intent(context, context.getClass());
refresh.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY|Intent.FLAG_ACTIVITY_NO_ANIMATION);
context.startActivity(refresh);
What am I doing wrong ? How can I either make the updateConfiguration restarts the activity or make the manual intent not create an history ?
Instead of relaunching the activity, you want to setContentView() once again after you change locale to change activity layout, or reinflate the fragment
convertView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
I18N.setLang(context, "fr");
//setContentView(R.layout.act_layout); // for activity
convertView = inflater.inflate(R.layout.list_item, null)
};
});
edit
Your convertView would be final so you can refresh it through a method
protected View refresh()
{
//inflate
View convertView = inflater.inflate(R.layout.list_item, null);
//setup ui controls
convertView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
I18N.setLang(context, "fr");
refresh();
};
});
return convertView ;
}
public View getChildView(final int groupPosition, final int childPosition,
boolean isLastChild, View convertView, ViewGroup parent)
{
return refresh();
}
I have added android:noHistory="true" to my settings activity in the AndroidManifest.xml
This way, i can go back to the main screen after changing the language.
Related
I am new to android please help me to play audio on list item click in fragment. I am using this code but on item click my app gets crash showing error.
I have tried the below code but my app crashes on click on item:
public class FragmentOne extends Fragment {
static MediaPlayer mediaPlaye;
int audioIndex;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.fragment_fragment_one, container, false);
ListView audioView = view.findViewById(R.id.listview1);
final ArrayList<String> audioList = new ArrayList<>();
String[] proj = {MediaStore.Audio.Media._ID, MediaStore.Audio.Media.DISPLAY_NAME};
final Cursor audioCursor = getActivity().getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, proj, null, null, null);
if (audioCursor != null) {
if (audioCursor.moveToFirst()) {
do {
audioIndex = audioCursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME);
audioList.add(audioCursor.getString(audioIndex));
} while (audioCursor.moveToNext());
}
}
audioCursor.close();
final ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(),R.layout.tectcolor, audioList);
audioView.setAdapter(adapter);
audioView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Uri u = Uri.parse(audioList.get(i).toString());
mediaPlaye = MediaPlayer.create(getContext(), u);
mediaPlaye.start();
}
});
return view;
}
}
I am expecting to play audio on item click in fragment.
u must setOnclick in your Custom Adapter.
when u set onClick on your List, Android check all of your view of List and it will cause a crash.
create an adapter and override onClick in there.
have good coding...
I have tab1 and tab3 also these have their classes and I want to click button in tab1 and change textview in the tab3, but I couldn't find anyway.
This is my tab1 class
public class tab1Contacts extends Fragment{
TextView tv;
EditText et;
TextView tv3;
personInfo pı;
public personInfo returnpı(){
return pı;
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.tab1contents, container, false);
Button btn_jog = (Button) rootView.findViewById(R.id.jogging_button);
tv = (TextView) rootView.findViewById(R.id.newRecordText);
et = (EditText) rootView.findViewById(R.id.durationtext) ;
pı = new personInfo();
pı.eyesPower = 100;
pı.brainPower = 100;
pı.armsPower = 100;
pı.legsPower = 100;
pı.hearthPower = 100;
pı.energyLevel = 100;
pı.calorie = 2000;
pı.condition = 0;
btn_jog.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
int duration = Integer.parseInt(et.getText().toString());
pı.jogging(duration);
//I want to change here textview in the tab3.
}
});
return rootView;
}
}
This also my tab3 Class:
public class Tab3Contacts extends Fragment {
TextView tv3;
double newBrainpower;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.tab3contents, container, false);
tv3 = (TextView) rootView.findViewById(R.id.list_text) ;
return rootView;
}
}
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//pager.setCurrentItem(yourindex);// if you use pager
getTabHost().setCurrentTab(yourindex);
}
});
If I'm reading your question correctly, then what you need is for tab3 to listen to events from tab1. For that you will want to implement some kind of internal notification/eventing system. This is typically handled through a notification handling class that will register observers/listeners.
An example from a project I've been maintaining:
public class NotificationManager {
public interface Observer {
public void update(String notificationName, Bundle data);
}
private static NotificationManager singletonNotifier = null;
private HashMap<String, ArrayList<Observer>> mObservables = null;
private NotificationManager() {
mObservables = new HashMap<String, ArrayList<Observer>>();
}
//enforce singleton
public static NotificationManager getInstance() {
if (singletonNotifier == null) {
singletonNotifier = new NotificationManager();
}
return singletonNotifier;
}
public void addObserver(String notificationName, Observer observer) {
// add to map
// in multi-threaded apps make sure you use synchronized or a mutex
}
public void removeObserver(String notificationName, Observer observer) {
// remove from map; mind threading
// overload as necessary for your design
}
public void notifyObservers(String notificationName, Bundle data) {
// go through your map of observers, build an array of observers
// that need to update, then for each observer, call
// observer.update(notificationName, data);
}
}
Then your tab3 class would need to implement the Observer interface and on object construction register itself with the NotificationManager with a string value for the type of notification it wants (use best practices for constants instead of string literal arguments), using the call
NotificationManager.getInstance().addObserver("Tab1DataChange", this);
It will need to implement the update(String, Bundle) method, which will make all the changes that you need.
Then in the class for the tab1 object, add to the click listener this call:
NotificationManager.getInstance().notifyObservers("Tab1DataChange", data);
Where data is any information that observers would need to know to respond. In keeping with the idea of decoupling code, do not put together a data bundle that is explicitly for one listener, because at some point you might need something else to listen for the same event. Save yourself some grief now by designing the data bundle to contain what would need to update regardless of who is consuming the event.
Some lessons learned for me:
Pay attention to Android lifecycle. OnPause and OnDestroy for the active view(s) should unregister the listener so that you don't end up with a null pointer exception if something triggers that event while the observer object is not available. OnCreate and OnResume should reregister. In some cases I have been able to not worry about OnPause/OnResume, but depending on your app you may need them.
Question:
I'm adding views into a recyclerview with a click. When I click a view it opens a DialogFragment, how do I remove that view through the DialogFragment (by clickign on a button inside it)?
Adapter:
public class SubjectsAdapter extends RecyclerView.Adapter<SubjectsAdapter.ViewHolder> {
public List<String> items = new ArrayList<>();
public Activity mcontext;
public SubjectsAdapter(Activity context) {
this.mcontext=context;
}
public void addItem(String name) {
items.add(name);
notifyItemInserted(items.size() - 1);
}
public void removeItem(int position) {
items.remove(position);
notifyItemRemoved(position);
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(R.layout.grid_item_button, parent, false);
view.requestFocus();
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.setButtonName(items.get(position));
}
#Override
public int getItemCount() {
return items.size();
}
int i = 100;
public EditText EditName;
class ViewHolder extends RecyclerView.ViewHolder{
public Button GridButton;
public SharedPreferences prefs;
public ViewHolder(View itemView) {
super(itemView);
GridButton = (Button) itemView.findViewById(R.id.grid_button);
EditName = (EditText) itemView.findViewById(R.id.editName);
ClassName = (TextView) itemView.findViewById(R.id.ClassName);
prefs = mcontext.getPreferences(Context.MODE_PRIVATE);
GridButton.setId(++i);
EditName.requestFocus();
//Showing the DialogFragment
GridButton.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Fragment_Subject_Edit editFragment = Fragment_Subject_Edit.newInstance();
Bundle data = new Bundle();
data.putInt("ID", v.getId());
editFragment.setArguments(data);
editFragment.show(mcontext.getFragmentManager(), "Title");
return false;
}
});
}
public void setButtonName(String buttonName) {
GridButton.setText(buttonName);
}
}
}
Adding views in the activity:
recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
final SubjectsAdapter adapter = new SubjectsAdapter(this);
recyclerView.addItemDecoration(new SampleItemDecoration());
recyclerView.setAdapter(adapter);
recyclerView.setItemViewCacheSize(15);
recyclerView.setNestedScrollingEnabled(false);
#Override
public void onClick(View v) {
adapter.addItem(prefs.getString("key1", null));
}
What I have from Lucas Crawford answer, although I'm not getting it right:
1:
public Activity mcontext;
public View.OnLongClickListener LongClicking;
public SubjectsAdapter(Activity context, View.OnLongClickListener longClick) {
this.mcontext = context;
this.LongClicking = longClick;
}
2:
View.OnLongClickListener LongClicker;
...
...
...
adapter = new SubjectsAdapter(this, LongClicker);
recyclerView.setAdapter(adapter);
3:
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(R.layout.grid_item_button, parent, false);
view.setOnLongClickListener(LongClicking);
return new ViewHolder(view);
}
4:
fm = getFragmentManager();
ClassEditor = new Fragment_Subject_Edit();
LongClicker = new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Bundle data = new Bundle();
data.putInt("ID", v.getId());
ClassEditor.setArguments(data);
ClassEditor.show(fm, "Title");
return false;
}
};
Nothing happens when I longClick the button, nor anywhere in the view, what's wrong with these steps, and how to do number 5?
Easy Solution
Instead of assigning the onLongClickListener in the ViewHolder, I would assign the click listener as part of the constructor in the adapter's creation. This way, when you create the view holder in onCreateViewHolder, you give the new view for the view holder the adapter's passed click listener. This is better since it decouples the click event from the ViewHolder's work, as well as letting the activity that created the adapter handle what happens when clicked. (also the dialog fragment created is tied to an Activity lifecycle, why not create it there!).
Next, add a click listener to the dialog fragment's creation, possible as a member variable with a getter/setter. The button in the fragment is then assigned that click listener when you are inflating the dialog fragments view. This way, you do all the click event listening within your Activity, and use the same strategy in both the adapter and dialog fragment.
Can provide code IF required. I hope that makes sense.
Here is a list of what I suggested:
Add a click listener to the constructor of the recycler view adapter.
Pass an onLongClickListener object when you are creating the adapter within your activity.
Assign your view the click listener passed to the adapter in onCreateViewHolder.
Within the click listener created in your activity for the adapter, create the dialog fragment and assign a NEW click listener to handle the button press you desire. This click listener also is apart of the Activity.
When inflating the views in the dialog fragment, give the target button the listener you assigned to the dialog fragment.
The logic of removing the view (or item) from the RecyclerView adapter would be in the click listener you assign to the new dialog fragment. You need a reference to the item currently being removed as well (which you do via arguments).
Advanced Solution
A more advanced solution would be to use something like EventBus which handles listening for events. It cleans up a lot of code to use this. Another one is Otto by Square. It does the same thing, and I personally use Otto for event driven listening rather than passing around click listeners. You can decouple the need for a click listener being passed to your recycler's adapter by just setting a click listener in the adapter that posts an event that your activity is listening for, which then triggers the dialog fragment creation. The dialog fragment would then do the same thing by creating and assigning a new listener within the fragment that posts another event that your activity is listening for related to removing the particular adapter item.
I have written a small app that has a ListView with a custom adapter. Each row contains some Buttons, which will change background color when clicked, and I got the list items to be clickable as well by putting
android:descendantFocusability="blocksDescendants"
in the xml of the list items. But now I have this weird bug where clicking on the list item reverts all clicked Buttons back to their original colorless state. How can I get the Buttons to keep their color?
Details:
Part of the custom adapter:
View.OnClickListener onButtonClicked = new View.OnClickListener() {
#Override
public void onClick(View button) {
View listItem = (View) button.getParent();
final long DBid = (long) listItem.getTag();//database ID
final Button b = (Button) button;
sqldataDataSource datasource = new sqldataDataSource(context);
datasource.open();
datasource.updateButton(DBid);
datasource.close();
b.setBackgroundColor(0xFF386F00);
}
};
As you can see, I change the background color AND change the database entry, so when the whole list is reloaded, the Button keeps its color (another part of my custom adapter):
public View getView(int i, View convertView, ViewGroup parent) {
LayoutInflater inflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.hrlistitems, parent, false);
Button b = (Button) rowView.findViewById(R.id.HRlistB);
b.setOnClickListener(onButtonClicked);
if(!(values.get(i).getB().equals(""))){
b.setBackgroundColor(0xFF386F00);
}
return rowView;
}
This works fine when going to another activity and coming back to this one. The buttons are created colored as expected.
So my guess was that the list is recreated from the original listItem array when an item is clicked, which is why I tried to fix this by reloading my database, like so (from my activity):
#Override
protected void onStart() {
super.onStart();
datasource = new sqldataDataSource(this);
datasource.open();
listItems = datasource.getOnlyRoutes(id);//this works fine
Collections.sort(listItems, HallenRoute.vergleichen());
if (mListView == null) {
mListView = (ListView) findViewById(R.id.listViewHalle);
}
adapter=new customAdapter(this, listItems);
setListAdapter(adapter);
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int pos, long nid) {
listItems.get(pos).increaseCount();
datasource.updateCountHR(listItems.get(pos));
listItems = datasource.getOnlyRoutes(id);//fix I tried, doesn't work
Collections.sort(listItems, HallenRoute.vergleichen());
adapter.notifyDataSetChanged();
}
});
}
But this doesn't work.
How can I get the ListView to either not reload on ItemClick or reload properly (i.e. from database)?
You don't have to reload the whole data for every Button click.
In your Button click you're just updating the data base and not your adapter dataset values, this is why you always get the old background color.
public View getView(int i, View convertView, ViewGroup parent) {
LayoutInflater inflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.hrlistitems, parent, false);
Button b = (Button) rowView.findViewById(R.id.HRlistB);
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View button) {
View listItem = (View) button.getParent();
final long DBid = (long) listItem.getTag();//database ID
final Button b = (Button) button;
sqldataDataSource datasource = new sqldataDataSource(context);
datasource.open();
datasource.updateButton(DBid);
datasource.close();
//b.setBackgroundColor(0xFF386F00); no need for this line, getView() method will take care of the background
//update your adapter dataset, eg: values.get(i).setB("newColor");
notifyDataSetChanged(); // to refresh your adapter
}
});
if(!(values.get(i).getB().equals(""))){
b.setBackgroundColor(0xFF386F00);
}
return rowView;
}
PS: It's better if you save your "database ID" in your Model object not as a View tag.
I have a trouble with clickable elements, for example I can click two items in ListView at the same time with two fingers.
code for listview smth like this:
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.list_item, null);
convertView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//open some Activity here
}
}
}
}
So with two fingers Activty opens twice.
The same behaviour if I click to several buttons.
The same if click on button and some tab, and so on...
It is some global solution without using boolean flag?
You'll need to set an OnItemClickListener via setOnItemClickListener(...) in order to get the correct clicking behavior.
The easy way of handle this is:
long clickedTime;
#Override
public void onItemClick(View v) {
//open some Activity here
if (System.currentTimeMillis() - clickedTime > 100) {
clickedTime = System.currentTimeMillis();
// ... your stufff
}
}
but you should be using OnItemClickListener
since you look to a global solution, you can add :
android:launchMode="singleTop"
to the properties of your activity in the manifest file,
it allow the activity to be launched only one time.
I'm talking about the activity that you aim to open in onClick of course