How to make a date spinner on Android - java

How do I create an Android spinner that open a date picker dialog?
I want a Spinner that looks like the one in the calendar app, as shown in the screenshot below.
What I've already tried:
I made a spinner in my layout XML and tried to set the onclick attribute to call a method that shows the dialog. When I put a method name in "on click", I got and error that says "The following classes could not be found:
- Spinner (Change to android.widget.Spinner, Fix Build Path, Edit XML)".
<Spinner
android:id="#+id/spinner1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="showDatePickerDialog" />
showDatePickerDialog is in the right class:
public void showDatePickerDialog(View v) {
DialogFragment newFragment = new DatePickerFragment();
newFragment.show(getFragmentManager(), "Date");
}
And this is DatePickerFragment (copied from another website):
import java.util.Calendar;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.os.Bundle;
import android.widget.DatePicker;
public class DatePickerFragment extends DialogFragment
implements DatePickerDialog.OnDateSetListener {
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the current date as the default date in the picker
final Calendar c = Calendar.getInstance();
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int day = c.get(Calendar.DAY_OF_MONTH);
// Create a new instance of DatePickerDialog and return it
return new DatePickerDialog(getActivity(), this, year, month, day);
}
public void onDateSet(DatePicker view, int year, int month, int day) {
// Do something with the date chosen by the user
}
}
SOLUTION:
I found the solution at another question.
I used a regular spinner in the layout file, then in my activity class, has this code:
dateSpinner = (Spinner)findViewById(R.id.spinner_date);
View.OnTouchListener Spinner_OnTouch = new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
showDatePickerDialog(v);
}
return true;
}
};
View.OnKeyListener Spinner_OnKey = new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
showDatePickerDialog(v);
return true;
}
else {
return false;
}
}
};
dateSpinner.setOnTouchListener(Spinner_OnTouch);
dateSpinner.setOnKeyListener(Spinner_OnKey);
It seems a little hacked, but it works. Does anyone know a cleaner way to do it? It would not let me setOnClickListener() or setOnItemClickListener() on the spinner.

I was able to achieve it very easily. It looks exactly like that of the calendar app :)
Use a TextView and give it a spinner style:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="#android:style/Widget.Holo.Spinner" />
For API 10 and below without the Holo Theme, use:
style="#android:style/Widget.Spinner"

I think in general what people do in this case is use a button and style it like a spinner (an approach here, I think a better approach, mostly just setting the style to android.R.attr.spinnerStyle), or they use a spinner but overload the click action to create a dialog.
Of course the hard part there is creating the custom dialog that has nice month/day/year spin-ie-things (they aren't spinners... they are kind of like slot machines or something) in them!
There's a few ways you can go about that, there is some google documentation here, and you can always just lift the android source code (although there are obviously drawbacks).

A button is the best way, you just need to change the button style.
I tried to set the default android spinner style to my button, but it doesn't work correctly.
So I made my own style (it's a copy of the default spinner style). Here it is:
`<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:drawable="#drawable/spinner_disabled_holo_light" />
<item android:state_pressed="true"
android:drawable="#drawable/spinner_pressed_holo_light" />
<item android:state_pressed="false" android:state_focused="true"
android:drawable="#drawable/spinner_focused_holo_light" />
<item android:drawable="#drawable/spinner_default_holo_light" />
</selector>
`
The images you will find in ..\sdk\platforms\android-%%\data\res\drawable.
This works fine for me. This is a simple way to make a button spinner style, like the spinner on Google Calendar.

Related

Date Picker In Edit Text - What's wrong in this code?

I tried running it, but the Edit Text View gave no respond on clicking. The Date Picker was not even opened. What is wrong in here?
Please do help me with an elaborated answer along with the full code since I'm a newbie. Thanks
XML
<EditText
android:id="#+id/add_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginLeft="32dp"
android:layout_marginTop="16dp"
android:ems="10"
android:hint="Enter Date"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/add" />
MainActivity
public class addScreen extends AppCompatActivity {
EditText dateFormat;
int year, month, day;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.add_screen);
{
dateFormat = findViewById(R.id.add_date);
final Calendar calendar = Calendar.getInstance();
dateFormat.setOnClickListener(v -> {
year = calendar.get(Calendar.YEAR);
month = calendar.get(Calendar.MONTH);
day = calendar.get(Calendar.DAY_OF_MONTH);
DatePickerDialog datePickerDialog = new DatePickerDialog(addScreen.this, (view, year, month, dayOfMonth) -> dateFormat.setText(SimpleDateFormat.getDateInstance().format(calendar.getTime())), year, month, day);
datePickerDialog.show();
});
}
}
}
As Ben P said in the comment above, EditText is focusable by default.
So if you want to stop it from being shown the keyboard and just listen to the click listener you may need to stop the focus this below line do this so add it EditText declaration in your xml
android:focusable="false"
and that's it your EditText now can listen to your click listener.
P.S. I would recommend for you to use the material TextInputLayout and TextInputEditText which has a great look and follows the modern UX when it comes to create and input field and shown it to your users.

How are is the seekbar in main.xml linked to the java file? Programming in Android

I'm taking an Android class based on this book:
http://www.deitel.com/Books/Android/AndroidforProgrammers/tabid/3606/Default.aspx
I'm working on the TipCalculator example, though it's been modified by the professor I think (to make it work with the new versions and he got the project most of the way done. It's TipCalculator-partial-layout.zip). I don't understand the concepts of how the stuff in the java file knows what seekbar to listen to. Can someone explain it to me? I've been told it has to do with the id, but I don't understand what that means.
This is a snippet from the main.xml file regarding the seekbar:
<SeekBar
android:id="#+id/customSeekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_span="2"
>
</SeekBar>
This is a snippet from the main.xml file regarding the seekbar:
(part of onCreate)
// get the SeekBar used to set the custom tip amount
SeekBar customSeekBar = (SeekBar) findViewById(R.id.customSeekBar);
customSeekBar.setOnSeekBarChangeListener(customSeekBarListener);
Then there's the listener object:
private OnSeekBarChangeListener customSeekBarListener =
new OnSeekBarChangeListener()
{
// update currentCustomPercent, then call updateCustom
#Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser)
{
// sets currentCustomPercent to position of the SeekBar's thumb
currentCustomPercent = seekBar.getProgress();
updateCustom(); // update EditTexts for custom tip and total
} // end method onProgressChanged
#Override
public void onStartTrackingTouch(SeekBar seekBar)
{
} // end method onStartTrackingTouch
#Override
public void onStopTrackingTouch(SeekBar seekBar)
{
} // end method onStopTrackingTouch
}; // end OnSeekBarChangeListener
In this xml:
<SeekBar
android:id="#+id/customSeekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_span="2"
>
There's a line that says "#+id/customSeekBar". That line approximately meands "add an android id with the name 'customseekbar'.
Then, in the java code, you're calling:
SeekBar customSeekBar = (SeekBar) findViewById(R.id.customSeekBar);
Which says: find the android view with the id "customSeekBar" (from the XML) and cast it as a SeekBar, since findViewById returns a more generic View.
Then you can call all your java methods on it, like assigning listeners.
Does this make sense?
It might be worth looking into an "android basics", "android for beginners" or "getting started with android" book.
If you change something in the XML files, Java classes are generated from that. If an item has an id you can look it up. The class where all the ids are stored is named R
So if you execute
SeekBar customSeekBar = (SeekBar) findViewById(R.id.customSeekBar);
You are getting the numeric id for customSeekBar from the R class and looking for the component with that id, which is your SeekBar. In the next line you just use that SeekBar and add a listener to it.
android:id="#+id/customSeekBar" declares the name of your SeekBar that we have used before. If you change it to e.g. android:id="#+id/foo" you would get your SeekBar with
SeekBar customSeekBar = (SeekBar) findViewById(R.id.foo);
If you put an element in your layout (e.g. SeekBar) with the id "#+id/customSeekBar", it will be mapped in the code to R.id.customSeekBar. This "magic" is done by the R automatically generated file. Thus findViewById(R.id.customSeekBar) can be used to reach exactly that layout element.
The R file (located in the /gen folder) can map layout elements (called views - for example R.id.myView), layouts themselves (R.layout.myLayout), predefined strings (R.string.my_string), and other elements you will learn in due time.

Seeking some insight understanding how a simpleCursorAdapter works

Hey guys I was just hoping someone could shed some light on how this code is working and more specifically the simpleCursorAdapter. The full program is an app that is a to-do list, it's a very simple tutorial the user can input data or "notes" and save to a sqlite data base using cursors and loaders.
So my problem is that there is a specific method that I'm having trouble grasping how it works and as a result I cannot manipulate the way the data is displayed. I think the problem lies in the fact that I just don't understand how the adapter is taking in a different layout than what is displayed and showing it all in a list view.
private void fillData() {
// Fields from the database (projection)
// Must include the _id column for the adapter to work
String[] from = new String[] { TodoTable.COLUMN_SUMMARY };
// Fields on the UI to which we map
int[] to = new int[] { R.id.label }; //I don't quite understand but I know it's just a value for the adapter
getLoaderManager().initLoader(0, null, this);
adapter = new SimpleCursorAdapter(this, R.layout.todo_row, null, from,
to, 0); /*This line specifically I don't understand how it is working.
R.layout.todo_row is a near blank xml, used when there are no "todos"
with no listviews. R.layout.todo_list has the listview's but when
assigned in the adapter it doesn't work.
setListAdapter(adapter);
}
Overall I'm trying to make 3 listviews side by side to read data from the DB and just play around. If anyone could help me out it would be very much so appreciated, thank you.
R.layout.todo_row
<?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" >
<ImageView
android:id="#+id/icon"
android:layout_width="30dp"
android:layout_height="24dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:src="#drawable/reminder" >
</ImageView>
<TextView
android:id="#+id/label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:lines="1"
android:text="#+id/TextView01"
android:textSize="24dp"
>
</TextView>
</LinearLayout>
and R.layout.todo_list
<?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"
android:orientation="vertical" >
<ListView
android:id="#android:id/list"
android:layout_width="110dp"
android:layout_height="200dp" >
</ListView>
<ListView
android:id="#+id/listMiddle"
android:layout_width="110dp"
android:layout_height="200dp"
android:layout_toRightOf="#android:id/list" >
</ListView>
<ListView
android:id="#+id/listRight"
android:layout_width="110dp"
android:layout_height="200dp"
android:layout_toRightOf="#id/listMiddle" >
</ListView>
<TextView
android:id="#android:id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/no_todos" />
</RelativeLayout>
The entire class is below
package de.vogella.android.todos;
import android.app.ListActivity;
import android.app.LoaderManager;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import de.vogella.android.todos.contentprovider.MyTodoContentProvider;
import de.vogella.android.todos.database.TodoTable;
/*
* TodosOverviewActivity displays the existing todo items
* in a list
*
* You can create new ones via the ActionBar entry "Insert"
* You can delete existing ones via a long press on the item
*/
public class TodosOverviewActivity extends ListActivity implements
LoaderManager.LoaderCallbacks<Cursor> {
private static final int ACTIVITY_CREATE = 0;
private static final int ACTIVITY_EDIT = 1;
private static final int DELETE_ID = Menu.FIRST + 1;
// private Cursor cursor;
private SimpleCursorAdapter adapter;
private SimpleCursorAdapter middleAdapter;
private SimpleCursorAdapter rightAdapter;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.todo_list);
this.getListView().setDividerHeight(2);
fillData();
registerForContextMenu(getListView());
}
// Create the menu based on the XML defintion
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.listmenu, menu);
return true;
}
// Reaction to the menu selection
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.insert:
createTodo();
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case DELETE_ID:
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
.getMenuInfo();
Uri uri = Uri.parse(MyTodoContentProvider.CONTENT_URI + "/"
+ info.id);
getContentResolver().delete(uri, null, null);
fillData();
return true;
}
return super.onContextItemSelected(item);
}
private void createTodo() {
Intent i = new Intent(this, TodoDetailActivity.class);
startActivity(i);
}
// Opens the second activity if an entry is clicked
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Intent i = new Intent(this, TodoDetailActivity.class);
Uri todoUri = Uri.parse(MyTodoContentProvider.CONTENT_URI + "/" + id);
i.putExtra(MyTodoContentProvider.CONTENT_ITEM_TYPE, todoUri);
startActivity(i);
}
private void fillData() {
// Fields from the database (projection)
// Must include the _id column for the adapter to work
String[] from = new String[] { TodoTable.COLUMN_SUMMARY };
String[] middleId = new String[] { TodoTable.COLUMN_ID };
// Fields on the UI to which we map
int[] to = new int[] { R.id.label };
int[] two = new int[] { R.id.label };
getLoaderManager().initLoader(0, null, this);
adapter = new SimpleCursorAdapter(this, R.layout.todo_row, null, from,
to, 0);
middleAdapter = new SimpleCursorAdapter(this, R.layout.todo_row, null, middleId,
two, 0);
setListAdapter(adapter);
// setListAdapter(middleAdapter);
}
#Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
menu.add(0, DELETE_ID, 0, R.string.menu_delete);
}
// Creates a new loader after the initLoader () call
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String[] projection = { TodoTable.COLUMN_ID, TodoTable.COLUMN_SUMMARY };
CursorLoader cursorLoader = new CursorLoader(this,
MyTodoContentProvider.CONTENT_URI, projection, null, null, null);
return cursorLoader;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
adapter.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
// data is not available anymore, delete reference
adapter.swapCursor(null);
}
}
So my problem is that there is a specific method that I'm having trouble grasping how it works and as a result I cannot manipulate the way the data is displayed.
The method:
adapter = new SimpleCursorAdapter(this, R.layout.todo_row, null, from, to, 0);
Well, let's break this constructor down by each parameter:
this, a Context. The Adapter needs a Context to inflate each row's layout.
R.layout.todo_row, the row's layout. Every record in your Cursor will be displayed in this layout. (Exactly how the Cursor is displayed depends on from and to.)
null, a Cursor. This holds all of the data that will be shown in your ListView.
from, an array of the essential Views in the rows layout.
to, an array of the essential columns from your Cursor.
0, flags for when and why the data should be refreshed.
The trick behind every thing is this: the ids in the fourth (from) must each match a View in the second parameter (R.layout.todo_row). The Strings in fifth parameter must each match a column name in your Cursor. The fourth (from) and fifth parameters (to) must match one-to-one, because each column is displayed in one View. That's it really.
As you may have realized by now, this note:
R.layout.todo_row is a near blank xml, used when there are no "todos" with no listviews.
is wrong, sorry. If you want to display a note when the Cursor is empty add:
<TextView android:id="#android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="No data"/>
to todo_list.xml as described in ListActivity's documenation. By using this "magic id" in your TextView, the note should automatically be shown or hidden when appropriate.
All of this only interacts view the first ListView (with the id: `android:id="#android:id/list"), you need to create new Cursors and Adapters to use the other ListViews. Hope that helps!
I haven't looked at the source code for SimpleCursorAdapter. However, it appears that it is mostly doing two things:
The query for your data, based on the params you provide in fillData.
Looping through the results and populating the list using your template.
In my debuggin, I did notice that it's pretty efficient about filling the list - it only allocates as many rows as are needed to display. As you scroll, it recycles them rather than free reallocate them.
It looks like your fillData code is good. You don't say what isn't working so perhaps it's elsewhere. I've never used onCreateLoader (but probably should), so can't comment on that.
I saw one minor problem: in your R.layout.todo_row, you forgot the orientation attribute.
I'm assuming that the code compiles and runs fine and you just want to know what's going on. Well, there are a few things you need to be aware of. The first one is that the ListView doesn't take a layout parameter, your activity does in setContentView. Your R.layout.todo_list is only used by the TodosOverviewActivity to create the "screen" or "look" of the activity, that is, 3 ListView views side by side. Since the activity is a ListActivity it will automatically look for an entry of type ListView with an id of #android:id/list to automatically hook up the list listeners (just saves you a bit of typing), so your other lists will pretty much just sit there until you hook them up yourself (don't use the same id for items on the same layout). If you need to access these other lists you'll need to use the findViewById method in your activity and search for the id of the list you want. For example, we can access the middle list using this:
ListView middleList = (ListView)this.findById(R.id.listMiddle);
Now that we have the list, we need something to show. The lists are completely empty and you need to bring in data from somewhere. In your case, the data comes from a Cursor object you get from a ContentProvider. The cursor contains only one column that matters to us, the TodoTable.COLUMN_SUMMARY column that has the text we want to display in the list. The problem is that a list doesn't have a clue of what to do with a Cursor since the only thing it does is put a view on the screen and scroll it up and down. The Cursor, on the other hand, has all the data you want to show but doesn't have a clue of what a View is, much less how to put all the data it contains inside one for the list to show. Now you have the SimpleCursorAdapter which is, like the name says, an adapter. It is used to make incompatible things work together. On one side you have a list that needs a view, on the other side you have a cursor with the data you want to show, so now you need an adapter that will map each piece of data to part of a view. The SimpleCursorAdapter will ask you for 4 things in particular. The first is the layout of the view to show on the list, that is, what should a list item look like. This is the R.layout.todo_row that tells the adapter what views should be created. In this case we only have an icon and a TextView with the id R.id.label. Second, it will ask you for the cursor that contains the data, which is being set inside the onLoadFinished method (it is null when the adapter is created). Third, it wants to know what columns on the cursor matter. This is the String[] from array that says it should look for the data in the TodoTable.COLUMN_SUMMARY. Last, it needs to know where in the view to put this data, and this is the int[] to that contains the id of the TextView you'll be using to display the text, R.id.label.
In summary, the adapter is like a map between the data from the cursor and the layout for the view. Now, when the list needs a view to show on screen it will ask the adapter to give it one. The adapter then either recycles or creates a view form the layout you provided, fetches the data for each piece of the layout from the cursor and gives it all ready to go to the list to put it on the screen.

Very simple ListPreference to play sound is not working. Any ideas? (tons of details)

Could someone maybe tell me what i'm doing wrong? I'm betting im missing one small thing. I've looked on the developer site and i've read some tutorials and i'm just not seeing what i did wrong.
I'm trying to use a ListPreference to decide which sound to play on a button click.
I have this at the top:
public String greensound;
Here's my OnClick code:
case R.id.green:
SharedPreferences prefs=PreferenceManager.getDefaultSharedPreferences(this);
greensound = prefs.getString("greensound", "gsone");
if (greensound == "gsone") {
mSoundManager.playSound(1);
} else if (greensound == "gstwo") {
mSoundManager.playSound(2);
} else if (greensound == "gsthree") {
mSoundManager.playSound(3);
}
break;
Here's my xml:
<ListPreference
android:title="Geen Button"
android:key="greensound"
android:summary="Select sound for the Green Button"
android:entries="#array/green_list"
android:entryValues="#array/green_list_values"
android:defaultValue="gsone">
</ListPreference>
//I also tried removing the default value here it didnt seem to change anything but, should it be removed?
here's my Settings.java:
package com.my.app;
import android.os.Bundle;
import android.preference.PreferenceActivity;
public class Settings extends PreferenceActivity {
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.settings);
}
}
and here's my array's if that will help at all:
//This is the one I want to display to the user
<string-array name="green_list">
<item>Sound One</item>
<item>Sound Two</item>
<item>Sound Three</item>
<item>Sound Four</item>
<item>Sound Five</item>
</string-array>
<string-array name="green_list_values">
<item>gsone</item>
<item>gstwo</item>
<item>gsthree</item>
<item>gsfour</item>
<item>gsfive</item>
</string-array>
Here's a logcat that kinda looked possibly related:
08-27 01:52:07.738: WARN/Resources(6846): Converting to string: TypedValue{t=0x12/d=0x0 a=2 r=0x7f090000}
08-27 01:52:07.748: WARN/Resources(6846): Converting to string: TypedValue{t=0x12/d=0x0 a=2 r=0x7f090000}
08-27 01:52:07.758: WARN/Resources(6846): Converting to string: TypedValue{t=0x12/d=0x0 a=2 r=0x7f090000}
For those that might ask here's my DDMS > File Explorer > Data > Data > packageName > SharedPreferences This is what was in there:
com.my.app_preferences.xml:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="redsound">rsone</string>
<string name="greensound">gsone</string>
</map>
_has_set_default_values.xml:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<boolean name="_has_set_default_values" value="true" />
</map>
This all really confuses me more because...It looks like greensound does == gsone so.... I don't understand whats wrong its not even playing the default sound. and yes i've tested all this code without the listpreference code and they work great. I'm not sure what's wrong
mSoundManager.playSound(1);
mSoundManager.playSound(2);
mSoundManager.playSound(3);
Change greensound == "gsone" to greensound.equals(gsone)
This is a very common error for new java programmers and people who don't have a good grasp on object oriented concepts. Here's a good references for comparing objects and values in java
http://leepoint.net/notes-java/data/expressions/22compareobjects.html
Also, why does everyone insist on implementing onClickListener in the main activity? Is there some new tutorial that suggests this or something?
instead do an anonymous inner class (unless you have a bunch of buttons and you want to set their click listeners programatically.
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new onClickListener(){
#Override
public void onClick(){
Preferences pref = MyActivity.this.getSharedPreferences()
String s = pref.getString("sound","gsone");
}
});

How do I clear ListView selection?

TL;DR: You choose an option from (a) my listview. Then, you change your mind and type something in (b) my edit text. How do I clear your listview selection and only show your edittext? (and vice versa)
I have an application with a listview of options as well as an edittext to create an own option. I need the user to either choose or create an option, but not both. Here's a drawing of my layout:
Whenever the user selects an option from the listview, I set it as "selected" by making it green, like so:
<?xml version="1.0" encoding="utf-8" ?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_selected="true"
android:drawable="#color/colorPrimary"/>
<item
android:state_selected="false"
android:drawable="#color/windowBackground" />
</selector>
(this is set as the background of my listview)
Problem: I want to unselect the listview option if the user decides to type in their own option since they can only have one option.
User selects an option from the listview
User decides they want to create their own option using the edittext
The listview option is unselected when they start typing their own
I've tried doing the following, but nothing unselects.
e.setOnEditorActionListener(new TextView.OnEditorActionListener()
{
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
for(int i=0; i<=5; i++){
listView.setItemChecked(i, false);
}
listView.clearChoices();
listView.requestLayout()
adapter.notifyDataSetChanged()
}
}
A very puzzling predicament, any help is appreciated!
Edit: here is the layout of the edittext:
<EditText
android:id="#+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/textView4"
android:layout_alignBottom="#+id/textView4"
android:layout_toEndOf="#+id/textView4"
android:layout_toRightOf="#+id/textView4"
android:color="#color/colorPrimary"
android:imeOptions="actionDone"
android:inputType="text"
android:textColor="#color/textColorPrimary"
android:textColorHint="#color/colorPrimary" />
Edit: here is the layout of the listview:
<ListView
android:id="#+id/listview"
android:layout_width="wrap_content"
android:layout_height="250dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="#+id/toolbar"
android:background="#drawable/bg_key"
android:choiceMode="singleChoice"
android:listSelector="#color/colorPrimary">
</ListView>
Long Story Short
ListView selector (android:listSelector) is designed to indicate a click event, but not selected items.
If a ListView selector is drawn (after first click) it won't dissapear without drastic changes in the ListView
Hence use only drawables with transparent background if no state is applied to it as a ListView selector. Don't use a plain color resource for it, don't confuse yourself.
Use ListView choice mode (android:choiceMode) to indicate selected items.
ListView tells which row is selected by setting android:state_activated on the row's root view. Provide your adapter with corresponding layout/views to represent selected items correctly.
TL/DR Solutions
You can hide/remove selector with one of the following:
Making the selector transparent getSelector().setAlpha(0)
Resetting the current adapter with setAdapter(myAdapter) (adapter might be the same)
Solutions that might or might not work, depending on the OS version:
Making the list view to refresh layout completely via requestLayout(), invalidate() or forceLayout() methods;
Making the list view to refresh layout via notifyDataSetChanged()
Theory
Well, the built-in selection in ListView is utterly tricky at a first glance. However there are two main distinctions you should keep in mind to avoid confusing like this - list view selector and choice mode.
ListView selector
ListView selector is a drawable resource that is assumed to indicate an event of clicking a list item. You can specify it either by XML-property android:listSelector or using method setSelector(). I couldn't find it in docs, but my understanding is that this resource should not be a plain color, because after it's being drawn, it won't vanish without drastic changes in the view (like setting an adapter, that in turn may cause some glitches to appear), hence such drawable should be visible only while particular state (e.g. android:state_pressed) is applied. Here is a simple example of the drawable that can be used as a List View selector
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="true"
android:drawable="#android:color/darker_gray" />
<item
android:drawable="#android:color/transparent" />
</selector>
For whatever reason you cannot use a Color State List as List View selector, but still can use plain colors (that are mostly inappropriate) and State List drawables. It makes things somewhat confusing.
After the first click on a List View happens, you will not be able to remove List View selector from the List View easily.
The main idea here is that List View selector is not designed to indicate selected item.
ListView choice mode
ListView choice mode is assumed to indicate selected items. As you might know, primarily there are two choice modes we can use in ListView - Single Choice and Multiple Choice. They allow to track a single or multiple rows selected respectively. You can set them via android:choiceMode XML-property or setChoiceMode() method.
The ListView itself keeps selected rows in it and let them know which one is selected at any given moment by setting android:state_activated property of the row root view. In order to make your rows reflect this state, their root view must have a corresponding drawable set, e.g. as a background. Here is an example of such drawable:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_activated="true"
android:drawable="#android:color/holo_green_light" />
<item
android:drawable="#android:color/transparent" />
</selector>
You can make rows selected/deselected programmatically using the setItemChecked() method. If you want a ListView to clear all selected items, you can use the clearChoices() method. You also can check selected items using the family of the methods: getCheckedItemCount(), getCheckedItemIds(), getCheckedItemPosition() (for single choice mode), getCheckedItemPositions() (for multiple choice mode)
Conclusion
If you want to keep things simple, do not use the List View selector to indicate selected items.
Solving the issue
Option 1. Dirty fix - hide selector
Instead of actually removing selector, changing layouts and implementing a robust approach, we can hide the selector drawable when it's needed and show it later when clicking a ListView item:
public void hideListViewSelector() {
mListView.getSelector().setAlpha(0);
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (mListView.getSelector().getAlpha() == 0) {
mListView.getSelector().setAlpha(255);
}
}
Option 2. Thoughtful way
Let's go through your code and make it comply the rules i described step by step.
Fix ListView layout
In your ListView layout the selector is set to a plain color, and therefore your items are colored by it when they are clicked. The drawable you use as the ListView background have no impact, because ListView state doesn't change when its rows are clicked, hence your ListView always has just #color/windowBackground background.
To solve your problem you need at first remove the selector from the ListView layout:
<ListView
android:id="#+id/listview"
android:layout_width="wrap_content"
android:layout_height="250dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="#+id/toolbar"
android:listSelector="#color/colorPrimary"
android:background="#color/windowBackground"
android:choiceMode="singleChoice"/>
Make your rows reflect activated state
In the comments you give your adapter as follows:
final ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, text1, listOfThings);
You also asked me if it's possible to keep using standard adapter to achieve desired behavior. We can for sure, but anyway a few changes are required. I can see 3 options for this case:
1. Using standard android checked layout
You can just specify a corresponding standard layout - either any of the layouts that use CheckedTextView without changed background drawable as the root component or of those that use activatedBackgroundIndicator as their background drawable. For your case the most appropriate option should be the simple_list_item_activated_1. Just set it as in your ArrayAdapter constructor like this:
final ArrayAdapter<String> adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_activated_1, android.R.id.text1, listOfThings);
This option is the closest to what i understand by 'standard' adapter.
2. Customize your adapter
You can use standard layout and mostly standard adapter with a small exception of getting a view for your items. Just introduce an anonymous class and override the method getView(), providing row views with corresponding background drawable:
final ArrayAdapter<String> adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1, listOfThings) {
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
final View view = super.getView(position, convertView, parent);
if (convertView == null) {
view.setBackgroundResource(R.drawable.list_item_bg);
}
return view;
}
};
3. Customize your layout
The most common way of addressing this issue is of course introducing your own layout for the items view. Here is my simple example:
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#drawable/list_item_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:padding="16dp">
<TextView
android:id="#android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</FrameLayout>
I saved it in a file /res/layout/list_view_item.xml Do not forget setting this layout in your adapter:
final ArrayAdapter<String> adapter = new ArrayAdapter(this, R.layout.list_view_item, android.R.id.text1, listOfThings);
Clearing selection
After that your rows will reflect selected state when they are clicked, and you can easily clear the selected state of your ListView by calling clearChoices() and consequence requestLayout() to ask the ListView to redraw itself.
One little comment here that if you want unselect the item when user start typing, but not when he actually clicks the return (done) button, you need to use a TextWatcher callback instead:
mEditText.addTextChangedListener(new TextWatcher(){
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (mListView.getCheckedItemCount() > 0) {
mListView.clearChoices();
mListView.requestLayout();
}
}
#Override
public void afterTextChanged(Editable s) {}
});
Hopefully, it helped.
I have a good solution to do that. Add EditText to your layout which contains on your ListView as this layout:
<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:focusable="true"
android:focusableInTouchMode="true"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ListView
android:id="#+id/list_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:choiceMode="singleChoice"
/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Comment"
android:layout_below="#id/list_view"
android:id="#+id/editText"
android:nextFocusUp="#id/editText"
android:nextFocusLeft="#id/editText"/>
</RelativeLayout>
Then initialize Boolean variable to check whether editText if focused or not for example use this : boolean canBeSelected = true;
Then after setting adapter use this code:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
if (canBeSelected) {
listView.setSelector(R.drawable.background);
listView.setSelected(true);
listView.setSelection(i);
} else {
if (!editText.isFocused()){
canBeSelected = true;
listView.setSelector(R.drawable.background);
listView.setSelected(true);
listView.setSelection(i);
}
}
}
});
editText.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
canBeSelected = false;
Drawable transparentDrawable = new ColorDrawable(Color.TRANSPARENT);
listView.setSelector(transparentDrawable);
listView.clearChoices();
listView.setSelected(false);
return false;
}
});
editText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (editText.isFocused()){
Drawable transparentDrawable = new ColorDrawable(Color.TRANSPARENT);
listView.setSelector(transparentDrawable);
listView.clearChoices();
listView.setSelected(false);
canBeSelected = false;
}
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
Drawable transparentDrawable = new ColorDrawable(Color.TRANSPARENT);
listView.setSelector(transparentDrawable);
listView.clearChoices();
listView.setSelected(false);
canBeSelected = false;
}
#Override
public void afterTextChanged(Editable editable) {
if (editText.isFocused()) {
Drawable transparentDrawable = new ColorDrawable(Color.TRANSPARENT);
listView.setSelector(transparentDrawable);
listView.clearChoices();
listView.setSelected(false);
canBeSelected = false;
}
}
});
}
Hope it works with you :)
Re-setting the adapter in the edittext listener worked for me:
editText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
listview.clearChoices();
listview.setAdapter(adapter);
Toast.makeText(getApplicationContext(),"Typing" + listview.getSelectedItemPosition(), Toast.LENGTH_SHORT).show();
}
#Override
public void afterTextChanged(Editable editable) {
}
});
I put the selected index in a toast to check if the item was correctly deselected.
Hope this works!!
Just call clear when you make the request for the second data set:
arrayAdapter!!.clear()
You load your first data set
The user select one elements,
This action highlight your item
For any reason you launch the reload of your data set (because edittext's value changed),
at this moment call, clear() on your adapter.
Then you retrieved your dataset, you send it to the arrayAdapter and
No one is selected .
This is because when you clear, it also clear the selected flag

Categories

Resources