i'm trying to get into MVVM pattern and my problem is that I'm not really sure if I use it correctly. View is responsible for all UI operations (like show stuff?) but what happens when we need to change something in the logic.
So what I really wanna do is, show a dialog with certain options, pick one and reload the app.
I have implement the functions in MainActivity class and i use mCountrySelection.show() when action needed.
public void createCountriesDialog()
{
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(MainActivity.this);
dialogBuilder.setTitle("Available Countries");
GridView gridView = new GridView(MainActivity.this);
final String[] countries = getResources().getStringArray(R.array.countries);
final String[] codes = getResources().getStringArray(R.array.codes);
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(getApplicationContext(), android.R.layout.simple_list_item_1, countries);
gridView.setAdapter(arrayAdapter);
dialogBuilder.setView(gridView);
dialogBuilder.setNegativeButton("Close", new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
}
});
mCountrySelection = dialogBuilder.create();
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
PreferencesManager.setCountry(countries[position], codes[position]);
getSupportActionBar().setTitle(PreferencesManager.getCountry());
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.a_main_frame, new ArticlesFragment(), "ArticlesFragment");
fragmentTransaction.commit();
mCountrySelection.dismiss();
}
});
}
public void createAboutDialog()
{
AlertDialog.Builder aboutBuilder = new AlertDialog.Builder(MainActivity.this);
aboutBuilder.setTitle("Top News v1.0");
aboutBuilder.setMessage("Simple application for displaying Top Headlines from newsapi.org.\n\nIcons made by Freepik from www.flaticon.com.");
aboutBuilder.setNegativeButton("Close", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
dialog.dismiss();
}
});
mAbout = aboutBuilder.create();
}
Make 1 interface ItemClick and implement this interface from where you are calling Dialog.
public interface ItemClick{
public void onClick(int position, String country);
}
Pass this interface reference to your dialog method
public void createCountriesDialog(ItemClick listner)
{
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(MainActivity.this);
dialogBuilder.setTitle("Available Countries");
GridView gridView = new GridView(MainActivity.this);
final String[] countries = getResources().getStringArray(R.array.countries);
final String[] codes = getResources().getStringArray(R.array.codes);
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(getApplicationContext(), android.R.layout.simple_list_item_1, countries);
gridView.setAdapter(arrayAdapter);
dialogBuilder.setView(gridView);
dialogBuilder.setNegativeButton("Close", new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
listener = null;
}
});
mCountrySelection = dialogBuilder.create();
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
listener.onclick(position, countries[position]);
PreferencesManager.setCountry(countries[position], codes[position]);
getSupportActionBar().setTitle(PreferencesManager.getCountry());
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.a_main_frame, new ArticlesFragment(), "ArticlesFragment");
fragmentTransaction.commit();
mCountrySelection.dismiss();
}
});
}
Then when you click on grid item, using interface reference call onclick method
Now when you will receive a callback in onClick(int pos , String country)
Using your view model make api call and reload screen.
Set listener =null when dialog dismiss to avoid memory leaks
1- Expose a mutable LiveData or Observer in your ViewModel
public MutableLiveData<Pair<String, String>> countryInfo = new MutableLiveData<>()
2- Pass the user selection to the ViewModel
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
viewModel.countryInfo.setValue(new Pair(countries[position], codes[position]))
mCountrySelection.dismiss();
}
3- Run your use-case (business logic) in your ViewModel (uploading info to server, saving it in database, etc.)
4- Update View through exposing another Observable (in this case the same countryInfo will work)
In MainActivity observe the countryInfo:
viewmodel.countryInfo.observe(this, new Observer<String>() {
#Override
public void onChanged(#Nullable final String newName) {
// Update the UI
PreferencesManager.setCountry(countries[position], codes[position]);
getSupportActionBar().setTitle(PreferencesManager.getCountry());
getSupportFragmentManager().beginTransaction();
.replace(R.id.a_main_frame, new ArticlesFragment(), "ArticlesFragment");
.commit();
}
});
P.S: It is considered best if you move this line to ViewModel as it is contain part of business logic:
PreferencesManager.setCountry(countries[position], codes[position]);
Related
I was recently working on a todo list application in android studio where the user will input text through an EditText, and then the String will be added to a listView as shown below.
public class MainActivity extends AppCompatActivity {
private ArrayList<String> items;
private ArrayAdapter<String> itemsAdapter;
private ListView lvItems;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lvItems = (ListView) findViewById(R.id.lvItems);
items = new ArrayList<>();
itemsAdapter = new ArrayAdapter<>(this,
android.R.layout.simple_list_item_1, items);
lvItems.setAdapter(itemsAdapter);
}
private void setupListViewListener() {
lvItems.setOnItemLongClickListener(
new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> adapter,
View item, int pos, long id) {
// Remove the item within array at position
items.remove(pos);
// Refresh the adapter
itemsAdapter.notifyDataSetChanged();
return true;
}
});
}
public void addTask(View v) {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
LayoutInflater inflater = this.getLayoutInflater();
final View dialogView = inflater.inflate(R.layout.dialog, null);
dialogBuilder.setView(dialogView);
final EditText edt = (EditText) dialogView.findViewById(R.id.edit1);
dialogBuilder.setTitle("Add a New Task");
dialogBuilder.setMessage("Enter what you want to do");
dialogBuilder.setPositiveButton("Add", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
String itemText = edt.getText().toString();
itemsAdapter.add(itemText);
edt.setText("");
}
});
dialogBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
}
});
AlertDialog b = dialogBuilder.create();
b.show();
}
}
But the problem I have is that when the user closes the activity, the state of the ListView will change back to it's original value.
I want the ListView to retain the Information that has been put inside it when the activity closes.
How do I go about this?
There are many ways to save data in your app.
You can use the following options:
SharedPreferences
Files
SQLite (with plain sql or with the framework Room)
For more information check this link : https://developer.android.com/training/data-storage/index.html
Usually for this situation using Sqlite
But you can use others way such as Shared preference or File
Like you can see here: https://developer.android.com/guide/topics/data/data-storage.html
My issue issue is this one: When I press long click on item, I have two views which appears.
See below my code for java class. PLease help.
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
Intent intent = new Intent(MyGoodsActivity.this, CurrentActivity.class);
Bundle bundle = new Bundle();
bundle.putLong(Record.BUNDLE_SHOP_ID, mShopId);
bundle.putLong(Record.BUNDLE_the_ID, mGoodsAdapter.getItemId(position));
intent.putExtras(bundle);
MyGoodsActivity.this.startActivity(intent);
}
});
mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, final int position, long id) {
AlertDialog.Builder dialog = new AlertDialog.Builder(MyGoodsActivity.this);
dialog.setTitle(R.string.tip);
dialog.setMessage(R.string.info_confirm_delete_this_item);
dialog.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
MyDB db = new MyDB(MyGoodsActivity.this);
db.deleteGoodsById(mGoodsAdapter.getItemId(position));
refreshListgoods();
}
});
dialog.setNegativeButton(R.string.ko, null);
dialog.show();
return false;
}
}
);
After analyse my code I have juste return true for resolve my issue.
dialog.setNegativeButton(R.string.ko, null);
dialog.show();
return true;
}
I'm making a simple to do list and I want to be able to click on the item and enter a new text which will then replace the text of the TextView in that cell. I've got the dialogAlert working, I just don't know how to grab the cell's TextView and change it
This is what the Activity looks like,
public class MainActivity extends AppCompatActivity {
private ListDataSource ds;
private ListView listViewToDo;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Context context = this;
Log.d("MainActivity","Attempting to create data source");
try {
ds = new ListDataSource();
}
catch(Exception e)
{
e.printStackTrace();
Log.d("MainActivity","Failed to create data source");
}
Log.d("Main Activity","Attempting to link empty list view to on screen view");
listViewToDo = (ListView)findViewById(R.id.listOfLists);
Log.d("Main Activity","Views linked, Attempting to set adapter to listView");
listViewToDo.setAdapter(new ListDataSourceAdapter(this, ds));
Log.d("Main Activity", "Successfully set Adapter");
// add button listener
listViewToDo.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View view, final int position, long id)
{
AlertDialog.Builder editItem = new AlertDialog.Builder(context);
final EditText edittext = new EditText(context);
editItem.setTitle("Change Item");
editItem
.setMessage("Set new todo item")
.setView(edittext)
.setCancelable(false)
.setPositiveButton("Submit", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
//what do I put here?
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
dialog.cancel();
}
});
AlertDialog alertDialog = editItem.create();
alertDialog.show();
}
});
}
}
ds.remove(position);
ds.add(position,edittext.getText().toString().trim())
ListDataSourceAdapter adapter = new ListDataSourceAdapter(this, ds)
listViewToDo.setAdapter(adapter );
adapter.notifDataSetChanged();
I'm new to android, started it about a month ago, and now I'm trying to make a "Shopping List" app for the sake of practice. In this app I have a ListView, where user can insert items via EditText above that ListView. When user longClick on item, ContextMenu with "Edit", "Delete" and "Mark" fields appears. I have already made "Delete" button work, but I still have problems with "Edit" function. To make this function work I created DialogFragment class, so when user presses the "Edit" button, this DialogFragment appears. This DF has EditText field, where we enter data we want to change. Here is DialogFragment class code:
public class AlertEdit extends DialogFragment {
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder bd = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
bd.setView(inflater.inflate(R.layout.alert, null))
.setTitle("Edit")
.setPositiveButton(R.string.save, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
((MyActivity)getActivity()).doPositiveClick();
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
((MyActivity)getActivity()).doNegativeClick();
}
});
return bd.create();
}
as you can see, we have positive button here, which calls doPositiveClick method from MyActivity, which appears to be the main activity.
.setPositiveButton(R.string.save, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
((MyActivity)getActivity()).doPositiveClick();
}
So, here is the MyActivity class code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
lw = (ListView) findViewById(R.id.listView);
edtxt = (EditText) findViewById(R.id.editText);
alertEd = (EditText) findViewById(R.id.alertEdit);
goods = new ArrayList<String>();
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, goods);
lw.setAdapter(adapter);
lw.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> adapter, View v,
int position, long id) {
}
});
registerForContextMenu(lw);
edtxt.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction()== KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_ENTER) {
goods.add(0, edtxt.getText().toString());
adapter.notifyDataSetChanged();
edtxt.setText("");
return true;
}
}
return false;
}
});
}
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo info){
super.onCreateContextMenu(menu, v, info);
getMenuInflater().inflate(R.menu.actions, menu);
}
public boolean onContextItemSelected(MenuItem item) {
position = (int) info.id;
switch (item.getItemId()) {
case R.id.cnt_mnu_delete:
goods.remove(position);
adapter.notifyDataSetChanged();
return true;
case R.id.cnt_mnu_edit:
}
return super.onContextItemSelected(item);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.my, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public void doPositiveClick()
{
}
public void doNegativeClick()
{
}
public void showDialog()
{
DialogFragment frag = new AlertEdit();
frag.show(getFragmentManager(), "edit");
}
}
My problem is that I have no idea how to create that Edit function. I tryied to use AdapterContextMenuInfo, but it works only in onContextItemSelected method, because it requires and Item to work with. Hope you help me and sorry for the possible lack of information, ask me any additional questions please.
P.S. I'm trying to make this dialog for almost two weeks already and I'm really frustrated because of that.
I'm using this method - it's simple and you may adapt it to your needs:
First of all make an interface to handle your result, for example:
public interface OnDialogResultListener {
public void onDialogResult(String result);
}
Then use your dialog with additional view, like this:
public void showDialogAndGetResult(final int title, final String message, final String initialText, final OnDialogResultListener listener) {
// additional View - use appropriate View to your needs:
final EditText editText = new EditText(this);
editText.setText(initialText);
new AlertDialog.Builder(MainActivity.this)//
.setTitle(title)//
.setMessage(message)//
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
if (listener != null) {
listener.onDialogResult(editText.getText().toString());
}
}
})//
.setNegativeButton(android.R.string.cancel, null)//
.setView(editText)//
.show();
}
At last implement this interface in your activity:
public class YourActivity Extends Activity implements OnDialogResultListener{
...
#Override
public void onDialogResult(String result) {
//do what you need
}
...
}
Edit:
You may replace EditText by any View, including Layouts.
Still you may use the same scheme to return result from your DialogFragment descendant - just pass OnDialogResultListener in constructor or initializing method. I would say AlertDialog is more lightweight and DialogFragment allows more control and you may use both according to your needs.
I'm having a problem with my Android app. It's probably a simple Java problem, but I don't know Java well enough to know what to google. Below is a simplified version of what I have that illustrates my problem. I want to be able to access gridview from my dialog. Clearly what I have here isn't right. I tried moving the declaration outside of the onCreate method, but then my application crashes right at the start (there's no error message). I also tried adding it as an argument to ShowDialog, but I guess since I'm overriding that function that didn't work. As you can probably tell, I don't know what I'm doing. Thoughts?
public class HelloGridView extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final GridView gridview = (GridView) findViewById(R.id.gridview);
gridview.setAdapter(new ImageAdapter(this));
((ImageAdapter) gridview.getAdapter()).initializemThumbIds();
gridview.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
showDialog(0);
}
});
}
protected Dialog onCreateDialog(int id) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("?")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
gridview.setAdapter(new ImageAdapter(this)); // gridview cannot be resolved
dialog.cancel();
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
HelloGridView.this.finish();
}
});
AlertDialog diag = builder.create();
return diag;
}
}
I think you just don't have the correct scope, so below I made gridView a member variable. But I think what you really want to do is make your ImageAdapter a member variable and update the ImageAdapter only in your Dialog "yes" click.
public class HelloGridView extends Activity {
private GridView gridview;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
gridview = (GridView) findViewById(R.id.gridview);
gridview.setAdapter(new ImageAdapter(this));
((ImageAdapter) gridview.getAdapter()).initializemThumbIds();
gridview.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
showDialog(0);
}
});
}
protected Dialog onCreateDialog(int id) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("?")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
gridview.setAdapter(new ImageAdapter(this)); // gridview cannot be resolved
dialog.cancel();
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
HelloGridView.this.finish();
}
});
AlertDialog diag = builder.create();
return diag;
}
}