ActionbarSherlock list navigation - scroll to top/focus first row - android

I need some help with sherlock list navigation.
Goal: when you open navigation, I want scroll to top/focus to first row, so 1st item is always visible.
Problem: now when list has more items as screen can display(usually in landscape mode), when i select ie 4th item and then open list, first item is not visible, it's focused to last selected item.
It works for me with code bellow in custom spinner, but when I tried override same methods in IcsSpinner it haven't worked.
Code:
/**
* Cusrom spinner class - when open always focus on first item
*
*/
class CustomSpinnerSelection extends Spinner {
private boolean mToggleFlag = true;
//some constructors here
#Override
public int getSelectedItemPosition() {
// this toggle is required because this method will get called in other
// places too, the most important being called for the
// OnItemSelectedListener
if (!mToggleFlag) {
return 0; // get us to the first element
}
return super.getSelectedItemPosition();
}
#Override
public boolean performClick() {
// this method shows the list of elements from which to select one.
// we have to make the getSelectedItemPosition to return 0 so you can
// fool the Spinner and let it think that the selected item is the first
// element
mToggleFlag = false;
boolean result = super.performClick();
mToggleFlag = true;
return result;
}
}

There is one possible way of doing so, now that you've done it with spinner.
You can use custom view for the action bar, that contains a spinner for list navigation, and use your custom spinner class instead of the default spinner.
The custom view xml might look like this:
<?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="match_parent"
android:orientation="vertical" >
<Spinner
android:id="#+id/actionBarSpinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:entries="#array/default_list" />
</LinearLayout>
You will need to enable and set custom view from code.
View view = this.getLayoutInflater ().inflate (R.layout.actionbar_customview, null);
actionBar.setCustomView (view);
actionBar.setDisplayShowCustomEnabled (true);
Also, you can save the spinner to set any listeners or do any other operations.
Spinner actionBarSpinner = ((Spinner) view.findViewById (R.id.actionBarSpinner));
Just instead of Spinner, it would be your CustomSpinner class.

I'm not sure if this is your case, but one thing I found out about spinners: sometimes if you populate them on onCreate(), a little bit of low-level trickiness happens. Remember that Android is event-based, so putting the data in onCreate actually might not fill the spinner. The spinner sometimes gets filled after the screen is drawn!
So, if this is happening, try doing in a call after onCreate like onWindowFocused or whatever it's called

setSelection might be the only way to load the spinner with the first item focused. Try this:
Spinner s = (Spinner) findViewById(R.id.spinner_id);
int i = adapter.getPosition("blue");
s.setSelection(i);

Related

Android - selectable recyclerview item with selectable views in it

I have got a RecyclerView item looking like this
I want to achieve that when I click on item the ImageView will get overlay over it and TextView will become bold. I know how to use adapter and where to handle item clicks. I also know how to make overlay or bold text. I only want to know how to make this item selectable to get the behavior I described above. Because I found only tutorials to change background of item when clicked.
Based on this
I only want to know how to make this item selectable to get the behavior I described above.
So basically you need a way to tell the ViewHolder that the current item is selected, such that in onBindViewHolder() the items are rendered as per need.
I can think of this: Make a model of the item youre adding to the RecyclerView. Add a key as boolean isSelected = false in it.
And inside your onBindViewHolder where youre implementing the onClick()interface. do this:
... new OnClickListener({
... onClick(){
// take the item and set the isSelected flag
list.get(position).setIsSelected(true):
notifyDataSetChanged();
// alternatively you can also toggle this flag.
}
});
and while loading inside onBindViewHolder to this:
if (list.get(position).isSelected()) {
// highlight aka set overlay and bold text to view
} else {
// as per recyclerview doc, reset the views.
}
All you need is having a variable to hold the selected index. Then decorating the selected item in onBindViewHolder() method.
int selectedIndex = 0;
...
public void onBindViewHolder(ViewHolder viewHolder, int position) {
if (selectedIndex == position) {
// Do things you want
}
}

Reset background color in Android ListView

In my QuestionsActivity, I am showing a question and bunch of answers. The question is displayed on TextView and answers are displayed on ListView that is composed of TextViews. There is an ActionButton named "Check Answer" and when it is clicked, it shows the correct answer by changing the background color of the TextView in the ListView.
The background changing code looks like this:
if (allAnswers.get(i).isCorrect())
{
mAnswerList.getChildAt(i).setBackgroundColor
(getResources().getColor(R.color.correct_answer_background));
return;
}
and now there are two Buttons at the footer section of this QuestionsActivity called PreviousQuestionButton and NextQuestionButton and they are basically navigation buttons between questions.
The problem is, when I go to the next question after clicking on "Check Answer" button, the answer background color doesn't go away and remains in the next question answer options. I tried invalidate(), refreshDrawableState() method of ListView but no luck!
This is the method which displays the answers for a given question:
private void showAnswers(int questionLocation)
{
int questionId = mAllQuestions.get(questionLocation).getQuestionId();
List<Answer> answers = mAnswerRepository.getAllByQuestionId(questionId);
mAnswerAdapter.clear();
for (int i = 0; i < answers.size(); i++)
{
mAnswerAdapter.add(mOptionLetters[i] + ". "
+ answers.get(i).getAnswerText());
}
mAnswerAdapter.notifyDataSetChanged();
}
My Question
What I want is that when I click on next or previous buttons, the background color of the correct answer in ListView should disappear so that next and previous question button can show non-selected answer options list to the user. Is there any method which resets ListView to a state which does not have any background applied?
For selected answer option, I am using mAnswerList.clearChoices() in order to unselect but it does not apply for correct answer background color.
Well, to reset the color you can very well hard-reset the adapter by creating a new one. So don't clear and add as that may keep the views in the state they were before. I am not too sure about this since I am not clearing or adding from an adapter, but always creating a new one to fulfill my new needs.
Anyway, another reason why things may not go in the direction you want is that the views may get recycled, since we're talking about a ListView. So if you want to highlight a list item, you should keep in the data model the information about highlight by initializing it to false and if the user selects one set the highlight state to true. I suppose the Answer class has as a minimum the following:
public class Answer {
private String data;
private boolean correct;
public String getData() {
return data;
}
public boolean isCorrect() {
return correct;
}
#Override
public String toString() {
return data;
}
}
So your adapter could look close to this - getView method is the most important to notice (don't forget to set to default background if the answer is incorrect or the adapter should not highlight correct answer):
public class MyAdapter extends ArrayAdapter<Answer> {
private boolean showCorrectAnswer;
private List<Answer> modelAnswers;
public MyAdapter(Context context, List<Answer> answers) {
super(context, android.R.layout.simple_list_item_1, answers);
this.modelAnswers = answers;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
if(showCorrectAnswer && modelAnswers.get(position).isCorrect()) {
view.setBackgroundColor(getContext().getResources().getColor(R.color.correct_answer_background));
} else {
view.setBackgroundColor(getContext().getResources().getColor(R.color.default_background));
}
}
public void setShowCorrectAnswer(boolean showCorrectAnswer) {
this.showCorrectAnswer = showCorrectAnswer;
notifyDataSetChanged();
}
}
What you need to do is to keep a reference to this custom adapter and if you need to highlight the correct answer or not simply call setShowCorrectAnswer(true / false);. It will trigger a redraw and in the getView() it will decide what to do based on adapter state and correct answer.
Hope it make sense ... I wrote all this while drinking a beer :)
Basically, I agree with the answer from payeli - you should change the background of selected answer in the "next/previous question" button.
But then there is this question:
"Is there any method which resets ListView to a state which does not have any background applied?"
Answer to this (as far as I know) is: not directly. But there are two workarounds.
If you call notfiyDataSetChange, not all views are completely redrawn. If possible, just the appropriate values are changed. It's mainly for performance reasons. (Imagine having 1000 contacts with images and names dumped and redrawn)
So how can you deal with this? (Other then reseting the background in the onClick method) Since you said these items are answers for a question, I assume you are not concerned about performance because there won't be too many of them.
Then you can simply:
A) Create new instance of Adapter instead of changing data in the old one. When you switch adapters, all views in list are removed, so, no recycling can happen in the new adapter.
B) Create custom Adapter, override getView method and make sure every time view is requested, you return a new view, so no recycling can happen.
Again: this really isn't "performance friendly" and should not be used with big lists with a lot of items!
If you want to know more, feel free to ask in comments or read the reference of Adapter class, especially the parameter convertView of getView method. (http://developer.android.com/reference/android/widget/Adapter.html)
As per my understanding U need to change color of text view in one of the list view row i.e row containing correct answer.
If this is your problem then On Click of button simply clear list view and recreate list view. Not when list view is recreated then check out position of row which contains correct answer. After getting row position simply change color of text view.
For Ex: In your Adapter class check as fallows in your getView();
for ( int i = 0; i < position.length ; i++ )
{
if(position == your_required_position){
{
textview.setColor(Color.BLUE);
}else{
textview.setColor(Color.BLACK);
}
}
In the onClickListener of "Check Answer" button, you need to reset the color:
void onClick (View v){
..............
mAnswerList.getChildAt(currentQuestion).setBackgroundColor
(getResources().getColor(R.color.default_answer_background));
}
you can save the default background somewhere, and when you press next/previous question button you should apply that color. Example:
TypedArray array = getTheme().obtainStyledAttributes(new int[] {
android.R.attr.colorBackground,
android.R.attr.textColorPrimary,
});
int backgroundColor = array.getColor(0, 0xFF00FF);
int textColor = array.getColor(1, 0xFF00FF);
array.recycle();
I found this code online to get background and text color (you can just keep the background part), when the onClick activates just set the background of your view to "backgroundColor"

Click event from checkbox in custom listview in Android

I have a custom layout for my list view rows:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#drawable/listSelector"
android:orientation="horizontal">
<LinearLayout
android:id="#+id/checkboxSelection1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="3dip">
<CheckBox android:id="#+id/checkbox1" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/checkbox1"
android:orientation="vertical">
<TextView
android:id="#+id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</RelativeLayout>
I've also got an adapter to get the appropriate data to display; which it does. From the UI perspective, it looks like I want it to.
However, when I click a checkbox - nothing happens. I want to store a list of the items I've selected in the backend (in the activity class ideally).
In my onCreate in the activity class, I've got this code:
listView.setAdapter(adapter);
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
// Click event for single list row
listView.setOnItemClickListener(new OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
int i = 1;
}
});
I've got the int 1 = 1; line there just so I could add a breakpoint to see if it gets hit. It doesn't. I'm sure I'm doing something wrong, like it's hooked up to the list view row instead of the checkbox or something - but I'm not sure how I can hook the event up to the checkbox.
If anyone could point me in the right direction I would appreciate it.
Thanks
Edit: JUST TO CLARIFY
I have this in the adapter:
taskChecked.setOnCheckedChangeListener(new OnCheckedChangeListener()
{
public void onCheckedChanged(CompoundButton arg0, boolean arg1)
{
// TODO Auto-generated method stub
int i = 1;
}
});
And that breakpoint does get hit. So I'm just trying to find out how I get have an event get raised in the activity, instead of just the adapter, when a checkbox is selected or unselected.
Don't do it!!! I almost went mad trying to get widgets in a ListView to respond to clicks. Do not put Button, ImageButton, or CheckBox widgets in a ListView. TextViews and ImageViews are the way to go. Trying to react to that click on that CheckBox to find what ListView item it is in and then send something to the Activity could be very harmful to your health. I tried.
TextView + ImageView that can vary between an icon showing a checkmark and one without - simulate a CheckBox;
ImageView by itself can simulate a Button.
The ImageView needs to be set to focus=false.
First, create a new class that has the fields you want to display for each item in the ListView. I made one with the text to display and a boolean that indicates whether it is checked or not. Use this class for your ArrayList and ArrayAdapter.
Then you add the setOnItemClickListener() for the ListView, then use the position to find the item view, and then get the item of your new class and toggle its boolean.
In the MyArrayAdapter.getView method, getItem(position) returns the instance of the new class for that item. Use the boolean to determine what icon to use for the ImageView.
When you need to know what is and isn't "checked" in the ListView, you just go through the ArrayList and check the boolean for each item.
I figured it out.
In the adapter I added this:
With _activity being the activity passed into the constructor from the calling activity. myObj is declared as a final earlier up in the code, based on the position in getView and the data passed in when the adapter was constructed.
taskChecked.setOnCheckedChangeListener(new OnCheckedChangeListener()
{
#Override
public void onCheckedChanged(CompoundButton button, boolean checked)
{
// Cast it so we can access the public functions
MyActivity myActivity = (MyActivity) _activity;
if (checked) // true if the checkbox is checked, false if unchecked
{
myActivity.checkboxSelected(myObj);
}
}
});
And in the activity I added this:
public void checkboxSelected(MyObj myObj)
{
// Do stuff with myObj here
}
Hopefully this helps someone.

Display default text in a SpinnerAdapter (For an ActionBar navigation)

I've had a look at the documentation, but I'm not sure what would be the best way to approach this.
This is what I'm doing:
mActionBar = getActionBar();
mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
SpinnerAdapter mSpinnerAdapter = ArrayAdapter.createFromResource(this,
R.array.platform_list,
android.R.layout.simple_spinner_dropdown_item);
What I'm trying to do is give the dropdown list a default value - "Please Select a Type", when the application loads up and nothing is selected yet. This is because the selection will trigger a change in the listview fragment displayed below, and without a selection (at startup) i would like to display a simple placeholder image or something.
Any ideas? Thanks a lot in advance!
Are you sure that what you want is a SpinnerAdapter? I spent a while today trying to figure out the same problem until I realised that what I actually wanted was just a SubMenu.
You can set the selection of the Spinner to a particular position which can display this text
public void setSelection (int position)
Since: API Level 1
Sets the currently selected item. To support accessibility subclasses that
override this method must invoke the overriden super method first.
Parameters
position Index (starting at 0) of the data item to be selected.
http://developer.android.com/reference/android/widget/AbsSpinner.html#setSelection(int)
Its not clear you problem to me.But i guess you want to view a default view and after select anything you want to change the below list view.If i am correct you can do this like this.
create onItemSelectedListener
class Ranger implements AdapterView.OnItemSelectedListener {
#Override
public void onItemSelected(AdapterView<?> parent,
View view, int pos, long id) {
item= parent.getItemAtPosition(pos).toString();
//if the selected item is default do default things
//or else do the list view update
}
}

Android: Spinner with changing ArrayAdapter - how to identify?

I want to have three Spinners with contents which depend on each other.
E.g. Spinner1 displays {item1, item2} and Spinner2 either {item3, item4} or {item5, item6} depending on whether item1 or item2 is selected on Spinner1.
The same I want for Spinner 3, which reacts to changes of Spinner1 and/or Spinner2.
For the latter, I have to determine first which of the possible value sets is shown atm in Spinner2.
My question is kind of similar to this question, but I don't know what to do after getting the adapter.
That's what I have so far:
ArrayAdapter adapter1 = (ArrayAdapter) spinner2.getAdapter();
if(items_spinner1[0].contentEquals(adapter1.getItem(0)))
{
//...
}
I get the adapter, ask for the first value and compare it to the first String value of my Array to identify it. It doesn't at all seem elegant to me. Is there an easier solution?
You say the contents of the later spinners depend on the selection in the earlier ones, but the code you posted depends only on the contents of a spinner.
adapter1.getItem(0) returns the first item in the list, not the currently selected item. To get the currently selected item, use the spinner's (not the adapter's) getSelectedItem() method.
You could, for example, put something like this in your first Spinner's onItemSelectedListener (edited based on your comment below):
public void onItemSelected (AdapterView<?> parent, View view, int position, long id) {
Object selectedItem = parent.getSelectedItem();
// Do this if the first Spinner has a set of options that are
// known in advance.
if (/*selectedItem is something*/) {
// Set up the second Spinner in some way
} else if (/*selectedItem is something else*/) {
// Set up the second Spinner in another way
}
// OR, if you need to do something more complex
// that would cause too much clutter here, do this.
fillSecondSpinner(selectedItem);
}
Then place something similar in the second Spinner's onItemSelectedListener. Get the selected items from the first and second Spinners using getSelectedItem() (or the item positions using getSelectedItemId() for the first and the position parameter for the second). Use the selected items to set up the third.
Edit: The OnItemSelectedListener for the second Spinner would look something like this.
// This must be defined in the enclosing scope.
final Spinner firstSpinner; // Must be final to be accessible from inner class.
Spinner secondSpinner;
// ...
secondSpinner.setOnItemSelectedListener(new OnItemSelectedListener {
public void onItemSelected (AdapterView<?> parent, View view, int position, long id) {
// Again, usually the selected items should be of
// a more specific type than Object.
Object firstSelection = firstSpinner.getSelectedItem();
Object secondSelection = parent.getSelectedItem();
fillThirdSpinner(firstSelection, secondSelection);
}
public void onNothingSelected (AdapterView<?> parent) { }
});

Categories

Resources