Android: Button within ListView not receiving onClick events - android

I am making a date picker activity that looks like a scrolling 30 day month/calendar (think Outlook calendar). The date picker contains a ListView (for scrolling) of MonthView views each of which is a TableView of the individual days. Each individual day in the MonthView is a button. When the MonthView is instantiated I walk each of the days (buttons) and attach a click listener:
final Button b = getButtonAt(i);
b.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v) {
setSelectedDate(buttonDayClosure, b);
}
});
setSelectedDate does a variety of things, but it also turns the button's background to yellow to signify the date is selected.
On my emulator, everything works as you would expect. Activity comes up, you press a day, the day turns yellow. No problems.
However, on some of my peer's emulators and on physical devices when you touch a day nothing happens... until you scroll the ListView... and then all of a sudden the selected day turns yellow. So, for example, you touch "the 3rd" and then nothing happens. Wait a few seconds and then scroll the ListView (touching an area of the calendar that is NOT the 3rd) and as soon as ListView scrolls the 3rd magically turns yellow.
On my peer emulators that show this behavior, I can set a breakpoint on the fist line of onClick and I see that the BP is in fact not hit until the ListView is scrolled.
This behavior doesn't make any sense to me. I would expect the onClick behavior to be unrelated to the encapsulating View's scrolling efforts.
Any thoughts on why this might be the case and how I can rectify the situation so that onClicks always happen immediately when the button is touched?
Thanks.
Post Scriptus: ArrayAdapter and ListView code requested:
public class MonthArrayAdapter extends ArrayAdapter<Date> {
private MonthView[] _views;
private Vector<Procedure<Date>> _dateSelectionChangedListeners = new Vector<Procedure<Date>>();
public MonthArrayAdapter(Context context, int textViewResourceId, Date minSelectableDay, Date maxSelectableDay) {
super(context, textViewResourceId);
int zeroBasedMonth = minSelectableDay.getMonth();
int year = 1900 + minSelectableDay.getYear();
if(minSelectableDay.after(maxSelectableDay))
{
throw new IllegalArgumentException("Min day cannot be after max day.");
}
Date prevDay = minSelectableDay;
int numMonths = 1;
for(Date i = minSelectableDay; !sameDay(i, maxSelectableDay); i = i.addDays(1) )
{
if(i.getMonth() != prevDay.getMonth())
{
numMonths++;
}
prevDay = i;
}
_views = new MonthView[numMonths];
for(int i = 0; i<numMonths; i++)
{
Date monthDate = new Date(new GregorianCalendar(year, zeroBasedMonth, 1, 0, 0).getTimeInMillis());
Date startSunday = findStartSunday(monthDate);
this.add(monthDate);
_views[i] = new MonthView(this.getContext(), startSunday, minSelectableDay, maxSelectableDay);
zeroBasedMonth++;
if(zeroBasedMonth == 12)
{
year++;
zeroBasedMonth = 0;
}
}
for(final MonthView a : _views)
{
a.addSelectedDateChangedListener(new Procedure<MonthView>()
{
#Override
public void execute(MonthView input) {
for(final MonthView b: _views)
{
if(a != b)
{
b.clearCurrentSelection();
}
}
for(Procedure<Date> listener : _dateSelectionChangedListeners)
{
listener.execute(a.getSelectedDate());
}
}
});
}
}
void addSelectedDateChangedListener(Procedure<Date> listener)
{
_dateSelectionChangedListeners.add(listener);
}
private boolean sameDay(Date a, Date b)
{
return a.getYear() == b.getYear() && a.getMonth() == b.getMonth() &&
a.getDate() == b.getDate();
}
#Override
public View getView (int position, View convertView, ViewGroup parent)
{
return _views[position];
}
private Date findStartSunday(Date d)
{
return d.subtractDays(d.getDay());
}
public void setSelectedDate(Date date)
{
for(MonthView mv : _views)
{
mv.setSelectedDate(date);
}
}
}
and
public class DatePicker extends ActivityBase {
public static final String CHOSEN_DATE_RESULT_KEY = "resultKey";
public static final String MIN_SELECTABLE_DAY = DatePicker.class.getName() + "MIN";
public static final String MAX_SELECTABLE_DAY = DatePicker.class.getName() + "MAX";
private static final String SELECTED_DATE = UUID.randomUUID().toString();
private long _selectedDate = -1;
private MonthArrayAdapter _monthArrayAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Date now = new Date();
Bundle inputs = this.getIntent().getExtras();
long min = inputs.getLong(MIN_SELECTABLE_DAY, 0);
Date minSelectableDate;
if(min == 0)
{
minSelectableDate = new Date(now);
}
else
{
minSelectableDate = new Date(min);
}
Log.i(DatePicker.class.getName(), "min date = " + minSelectableDate.toString());
long max = inputs.getLong(MAX_SELECTABLE_DAY, 0);
Date maxSelectableDate;
if(max == 0)
{
maxSelectableDate = new Date(now.addDays(35).getTime());
}
else
{
maxSelectableDate = new Date(max);
}
setContentView(R.layout.date_picker);
Button doneButton = (Button) findViewById(R.id.DatePickerDoneButton);
if(doneButton == null)
{
Log.e(this.getClass().getName(), "Could not find doneButton from view id.");
finish();
return;
}
doneButton.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v) {
Intent result = new Intent();
result.putExtra(CHOSEN_DATE_RESULT_KEY, _selectedDate);
setResult(RESULT_OK, result);
finish();
}
});
Button cancelButton = (Button) findViewById(R.id.DatePickerCancelButton);
if(cancelButton == null)
{
Log.e(this.getClass().getName(), "Could not find cancelButton from view id.");
finish();
return;
}
cancelButton.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v) {
setResult(RESULT_CANCELED, null);
finish();
}
});
ListView lv = (ListView)findViewById(R.id.DatePickerMonthListView);
lv.setDividerHeight(0);
_monthArrayAdapter =
new MonthArrayAdapter(this, android.R.layout.simple_list_item_1, minSelectableDate, maxSelectableDate);
_monthArrayAdapter.addSelectedDateChangedListener(new Procedure<Date>()
{
#Override
public void execute(Date input) {
_selectedDate = input.getTime();
}
});
lv.setAdapter(_monthArrayAdapter);
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState)
{
if(savedInstanceState.containsKey(SELECTED_DATE))
{
_selectedDate = savedInstanceState.getLong(SELECTED_DATE);
_monthArrayAdapter.setSelectedDate(new Date(_selectedDate));
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState)
{
savedInstanceState.putLong(SELECTED_DATE, _selectedDate);
}
}

Having the same problem, looking for an answer. I totally didn't believe it when I didn't get my onClick method until I scrolled my list. I'll post the answer here if I find it.
Right now, my best guess is to try different events besides click (because the scroll space is eating the complex touch events that turn into a click event):
"downView" is a static variable to track the element being clicked.
view.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
downView = v;
return true;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
if (downView == v) {
handleClick(v);
return true;
}
downView = null;
}
return false;
}
});

The main reason is that ListView doesn't like an adapter having an array of views.
So the problem is triggered by
public View getView (int position, View convertView, ViewGroup parent)
{
return _views[position];
}
When looking at the ListView code (or rather it's parents AbsListView.obtainView method) you'll see code like
if (scrapView != null) {
...
child = mAdapter.getView(position, scrapView, this);
...
if (child != scrapView) {
mRecycler.addScrapView(scrapView);
It can happen that getView(position,...) is called with scrapView != _views[position] and hence scrapView will be recycled. On the other hand, it is quite likely that the same view is also added again to ListView, resulting in views having a weird state (see this issue)
Ultimately, this should be fixed in ListView IMO, but temporarily, I advise against using an adapter containing an array of views.

So I'll add a completely separate answer to this outside of manually composing your own click events from touch events.
I traded some emails with the Android Team (there's a few perks from being consumed by the googly) and they suggested that my attempt to implement ListAdapter by hand was inefficient and that if I don't correctly hook up the data observer methods of the adapter it can cause "funny problems with event handling."
So I did the following:
1) Replaced my implementation of ListAdapter with a subclass of BaseAdapter that overrode the necessary functions.
2) Stopped using list.invalidateViews() and started using adapter.notifyDataChanged()
and the bug seems to have gone away.
That's more work than manually composing a click event, but it's also more correct code in the long run.

Aswer is:
public View getView(int position, View convertView, ViewGroup parent) {
View v=makeMyView(position);
v.setFocusableInTouchMode(false);
return v;
}

Related

Saving list data composed of individual buttons

I've made a list made of TextViews and Buttons, made that when a person clicks on a button, a fragment opens and there is a list of values he can select. The problem is when i press on another button to select a value again for a different field, the previous value disappears. So the question would be how to save the fragments values, and keep it saved until the app is closed ?
priceButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
PriceFragment priceFragment = new PriceFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.fragmentContainer, priceFragment).commit();
setToHideElements();
}
});
yearButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
YearFragment yearFragment = new YearFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.fragmentContainer, yearFragment).commit();
setToHideElements();
}
});
this is the year fragment
yearEndListView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Intent intent = new Intent(getActivity().getBaseContext(), MainMenuActivity.class);
String yearTo = yearList[i].toString();
int yearTint = Integer.valueOf(yearTo);
if (combinedYear != null) {
combinedYear = combinedYear + " " + yearTo;
intent.putExtra("Years", combinedYear);
getActivity().startActivity(intent);
} else {
combinedYear = null;
combinedYear = yearTo;
}
}
});
this is the method to retrive data
private void retriveDataFromFragment(){
Intent intent = getIntent();
String fragmentDataPrice = intent.getStringExtra("PriceData");
String fragmentDataYear = intent.getStringExtra("Years");
if(fragmentDataPrice != null){
priceButton.setText(fragmentDataPrice);
} else {}
if (fragmentDataYear != null){
yearButton.setText(fragmentDataYear);
} else {}
}
I use RetriveDataFromFragment method in OnResume method.
Thank you, for your time.
you are initiating every time a new fragment so it will never retain its state. you have to use listener while closing the fragment so you can get back your data.
I got the anwser if someone else needs a similar menu, all you have to do is create a class that extends Application, and include into your manifest (the part with application tags). From there you just use getters and setters and all is well.

Android: Update ListView from EditText

friends.
I have an app module that implements State pattern. Idea is:
1. User enter the Date -->
2. Screen changes to a ne state: dynamically inflates ListView and asks user to fill the list with some data
Problem is that when I call notifyDataSetChange() from update method 2nd time, EditText view doens't want to do its functions anymore.
This is a string from a logcat, that I think contains key of my problem.
09-10 10:33:38.937 26954-26954/com.example.android.turtleinfo D/ListView: change accessibility focus position = 0
Here are codes for everything.
1. My State Pattern Super Class
public abstract class ScreenState
{
public BuilderActivity activity;
public ScreenState(BuilderActivity activity) {
this.activity = activity;
//initializeScreen(activity);
}
public abstract void initializeScreen(Activity activity);
public abstract void switchState();
public abstract void updateText(View view);
public void addTextWatcher(EditText text) {
text.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
activity.state.updateText(v);
return true;
}
return false;
}
});
}
}
2. Implementation of initialize screen and updateData methods
#Override
public void initializeScreen(Activity activity) {
((BuilderActivity) activity).editText.setHint("Name");
addTextWatcher(((BuilderActivity) activity).editText);
textView = (TextView)activity.getLayoutInflater().inflate(R.layout.schedule_text_style, null);
textView.setHeight(100);
screenText = "End Date: " + new SimpleDateFormat("MMMM dd, yyyy", Locale.ENGLISH).format(((BuilderActivity) activity).data.getEndDate())+"\n\n";
textView.setText(screenText+"\n");
((BuilderActivity) activity).scheduleLayout.addView(textView);
((BuilderActivity) activity).data.setNames(new ArrayList<String>());
lv = (ListView)activity.getLayoutInflater().inflate(R.layout.listview_text_style, null);
listItems = new ArrayList<>();
adapt = new ArrayAdapter<>(super.activity, R.layout.schedule_text_style, listItems);
lv.setAdapter(adapt);
((BuilderActivity) activity).scheduleLayout.addView(lv);
}
#Override
public void updateText(View view) {
String text = activity.editText.getText().toString();
if (text.length() >= 2) {
if (!activity.data.names.contains(text)) {
activity.data.addToList(text);
listItems.add(text);
adapt.notifyDataSetChanged();
activity.editText.setText("");
counter++;
activity.editText.setText("");
} else {
Toast.makeText(activity, "same names are not allowed",
Toast.LENGTH_LONG).show();
((BuilderActivity) activity).editText.setText("");
}
}
else
{
Toast.makeText(activity, "name should contain 2 or more symbols",
Toast.LENGTH_LONG).show();
((BuilderActivity) activity).editText.setText("");
}
}
This is how it looks on device.
1. First insertion is ok
2. And second and further insertionas are impossible. OK softkeyboard button switches first to NEXT button
3. If I click next, EditText looses focus, nothing happens, but NEXT button Switches to '/n' button.
So, please, if anyone know, what am I doing worng, help!
Thanks in advance!
Your problem is in
public void addTextWatcher(EditText text)
function.
Look at the android:imeActionId and android:imeOptions attributes, plus the setOnEditorActionListener() method, all on TextView/Edittext.
You can also change the text of the keyboard button by using this.
mEditText.setImeActionLabel("Your text", KeyEvent.KEYCODE_ENTER);

Android GridView first button not working

I am having weird problems with Android GridView. I create a 3x4 grid and insert buttons into that grid. I want the background of the button to change when the user clicks that button. And this is working just fine for all buttons except the first one (the one with index 0 - top left). OnClick event listener doesn't fire at all for that button no matter what I do.
Here is the code where I create the view:
public View getView(int position, View convertView, ViewGroup parent) {
Button imageView;
if (convertView == null) { // if it's not recycled, initialize some attributes
Log.w("NOVO", "narejena nova celica");
imageView = new Button(mContext);
imageView.setPadding(8, 8, 8, 8);
} else {
Log.w("STARO", "stara celica");
imageView = (Button) convertView;
}
imageView.setEnabled(true);
int visina = parent.getHeight();
int sirina = parent.getWidth();
float dip = mContext.getResources().getDisplayMetrics().density;
float margin = 10*dip;
int view_height = (int)(visina - 3*margin)/4;
int view_width = (int)(sirina - 2*margin)/3;
int view_dim = 0;
if (view_height <= view_width)
view_dim = view_height;
else
view_dim = view_width;
imageView.setLayoutParams(new GridView.LayoutParams(view_dim, view_dim));
imageView.setId(position);
imageView.setOnClickListener(celice.get(position));
/*imageView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//Toast toast = Toast.makeText(mContext, v.getId() + "bla", Toast.LENGTH_SHORT);
//toast.show();
celice.get(v.getId()).celicaVisible(4000);
}});*/
celice.get(position).id = position;
celice.get(position).setButton(imageView);
return imageView;
}
If I replace
imageView = (Button) convertView;
with
imageView = new Button(mContext);
then the onClick() gets fired but the background still doesn't change. All the other buttons are working as expected.
And here is the custom class "Celica" that takes care of the actual work - changing the background...
public class Celica implements OnClickListener {
public boolean odkrit;
public boolean najden;
public int id;
public Drawable slikca1, slikca2;
public Celica par;
private Timer tim;
public Button but;
public Context con;
static int buttonsVisible = 0;
Celica(Drawable s1, Drawable s2) {
this.slikca1 = s1;
this.slikca2 = s2;
}
void celicaVisible(int millis) {
if (odkrit)
return;
Log.w("TEST", "prizganih " + buttonsVisible);
if (buttonsVisible >= 2)
return;
odkrit = true;
buttonsVisible++;
tim = new Timer();
tim.schedule(new timerDone(), millis);
((Activity)con).runOnUiThread(new Runnable() {
#Override
public void run() {
but.setBackground(slikca2);
}
});
}
void setButton(Button b) {
but = b;
((Activity)con).runOnUiThread(new Runnable() {
#Override
public void run() {
but.setBackground(slikca1);
}
});
}
class timerDone extends TimerTask {
#Override
public void run() {
if (!najden) {
odkrit = false;
((Activity)con).runOnUiThread(new Runnable() {
#Override
public void run() {
but.setBackground(slikca1);
}
});
}
buttonsVisible--;
tim.cancel();
}
}
#Override
public void onClick(View v) {
celicaVisible(4000);
}
}
In Android, ID of any View must be non zero and non negative number. means View ID should be > 0. and there is problem, when you are setting ID to the Button like
imageView.setId(position)
here ID of a button will be zero when position is zero(means first item). may be due to this, First Button's OnClickListener is not getting fired...try setting a ID that is greater than zero to Button and try once...
you can write like
imageView.setId(position+1) to ensure ID > 0
I actually figured it out. Everything works if I use the view that gets provided by the onClick() method instead of saving the actual button at the creation of the Celica object.
So basically adding:
but = (Button) v;
to the onClick() method solved the problem.

How to save/Restore state of Views of fragment on Tab change

I am following this tutorial.
there are 3 tabs in my App. in tab3 I m making changes to some views (like buttons and EditText spinners etc) and on the behalf of these changes i have to perform some actions in tab2. Simply you can say that i Change some values in tab3 and effect takes places in tab2. I know how to do this. I just want that my values of view becomes resets every time to default values when switching between the tab2 and tab3
My question is that how can i save the states of my views. so that on resuming the tabs i must get the default look of my views as i had left previously.
One thing more i tell you that i m doing all the work in onCreateView() methos. is this correct way. like this.
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Toast.makeText(getActivity(), "onCreateView", Toast.LENGTH_SHORT)
.show();
if (container == null) {
return null;
}
View vi = inflater.inflate(R.layout.settings, container, false);
btnInsert = (Button) vi.findViewById(R.id.btnInsert);
btnInsert.setOnClickListener(this);
btnPosition = (Button) vi.findViewById(R.id.btnPosition);
btnPosition.setOnClickListener(this);
txtPosition = (TextView) vi.findViewById(R.id.txtPosition);
txtLogo = (TextView) vi.findViewById(R.id.txtLogo);
imgLogoPreview = (ImageView) vi.findViewById(R.id.imgLogoPreview);
imgLogoPreview.setOnClickListener(this);
edTxtUserText = (EditText) vi.findViewById(R.id.edTxtPreview);
relLogo = (RelativeLayout) vi.findViewById(R.id.RelLogo);
relText = (RelativeLayout) vi.findViewById(R.id.RelText);
logoWheel = (WheelView) vi.findViewById(R.id.wheelLogo);
logoWheel.setAdapter(new ArrayWheelAdapter<String>(logoWheelList));
logoWheel.setVisibleItems(4);
logoWheel.setCurrentItem(1);
positionWheel = (WheelView) vi.findViewById(R.id.wheelPosition);
positionWheel.setAdapter(new ArrayWheelAdapter<String>(
positionWheelTextList));
// LogoWheel changed listener
changedListenerLogo = new OnWheelChangedListener() {
public void onChanged(WheelView wheel, int oldValue, int newValue) {
if (!wheelScrolled) {
}
}
};
logoWheel.addChangingListener(changedListenerLogo);
// Wheel scrolled listener
scrolledListenerLogo = new OnWheelScrollListener() {
public void onScrollStarts(WheelView wheel) {
wheelScrolled = true;
}
public void onScrollEnds(WheelView wheel) {
wheelScrolled = false;
btnInsert.setText(logoWheelList[wheel.getCurrentItem()] + "");
wheel.setVisibility(View.INVISIBLE);
if (wheel.getCurrentItem() == 2) {
txtPosition.setVisibility(View.INVISIBLE);
btnPosition.setVisibility(View.INVISIBLE);
relText.setVisibility(View.INVISIBLE);
relLogo.setVisibility(View.INVISIBLE);
} else if (wheel.getCurrentItem() == 1) {
relText.setVisibility(View.VISIBLE);
relLogo.setVisibility(View.INVISIBLE);
txtPosition.setVisibility(View.VISIBLE);
btnPosition.setVisibility(View.VISIBLE);
btnPosition.setText("Top");
positionWheel.setAdapter(new ArrayWheelAdapter<String>(
positionWheelTextList));
positionWheel.setVisibleItems(4);
positionWheel.setCurrentItem(1);
} else if (wheel.getCurrentItem() == 0) {
relLogo.setVisibility(View.VISIBLE);
relText.setVisibility(View.INVISIBLE);
txtPosition.setVisibility(View.VISIBLE);
btnPosition.setVisibility(View.VISIBLE);
btnPosition.setText("Top Left");
positionWheel.setAdapter(new ArrayWheelAdapter<String>(
positionWheelLogoList));
positionWheel.setVisibleItems(4);
positionWheel.setCurrentItem(1);
}
}
};
logoWheel.addScrollingListener(scrolledListenerLogo);
// /////////////////////Positon Wheel Listners///////////
// LogoWheel changed listener
changedListenerPosition = new OnWheelChangedListener() {
public void onChanged(WheelView wheel, int oldValue, int newValue) {
if (!wheelScrolled) {
}
}
};
positionWheel.addChangingListener(changedListenerPosition);
// Wheel scrolled listener
scrolledListenerPosition = new OnWheelScrollListener() {
public void onScrollStarts(WheelView wheel) {
wheelScrolled = true;
}
public void onScrollEnds(WheelView wheel) {
wheelScrolled = false;
String btnStatus = btnInsert.getText().toString();
if (btnStatus.equals("Logo")) {
btnPosition.setText(positionWheelLogoList[positionWheel
.getCurrentItem()] + "");
} else if (btnStatus.equals("Text")) {
btnPosition.setText(positionWheelTextList[positionWheel
.getCurrentItem()] + "");
}
wheel.setVisibility(View.INVISIBLE);
}
};
positionWheel.addScrollingListener(scrolledListenerPosition);
return vi;
}
at what point i must save the states and at what point i should retrieve the savedstates?
Please tell me the how to implement the lifecycle of fragment in simple words.
i also tried the saveInstance() method of fragment. but not called.
Thanks
If I understand you correctly then this might be useful. Instead of recreating Fragments each time you can hide and show them.
This of course preserves your Fragments so is possibly only something you'd do it you had a few tabs. The advantage of this is that
You don't need to worry about saving data and recreating the fragment
Changes are available immediately to the user as soon as the relevant tab is selected.

Combination of Service, ProgressBar, Gridview (and his notifydatasetchanged) an QuickAction-Implementation goes wrong

(Sorry for my mistakes in spelling)
I have a problem in the combination of some things:
I use a GridView to download show content from a database. onClick a file is downloaded which can be viewed later. Its about 20MB. This download is done by a service.
Every item in the gridView contains a progressbar in the layout. this progressbar is just shown if the item is donwloading in the background via the service.
The Service sends a broadcast about the download Progress, the Intent contains the ID of the item to find it in data of the Adapter for the gridview.
A BroadcastReceiver ist registered in my Activity to get the Progress Update (remember, simultaneous downloads are possible) which calls a function "setProgress" in the gridview-adapter
public void setProgress(long id, int progress)
{
for (int i = 0;i < list.size();i++)
{
if (list.get(i).getId() == id)
{
list.get(i).setProgress(progress);
notifyDataSetChanged();
}
}
}
To this point everything works fine.
Additionally im using QuickAction implementation from http:// www. londatiga.net/it/how-to-create-quickaction-dialog-in-android/ (Spaces because i cannot post more than two hyperlinks)
Now comes the problem:
Sometimes, i guess when notifydatasetchanged is called and the user taps on an item, the quickaction is shown on a wrong position.
To make this clearer here are two pictures:
The first one is what should happen (in this case a click on the first item of the gridview)
http://dl.dropbox.com/u/9031500/expected.png
The second picture shows what happens sometimes (only when some downloads are running, thats why i guess its because of the "notifydatasetchanged" and the rebuild of the views). This was also a click on the first item, unfortunately the quick-action is shown to the fourth item:
http://dl.dropbox.com/u/9031500/wrong.png
This is my implementation in my activity for the call of the quick-action:
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
showQuickAction(view, position);
}
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
showQuickAction(view, position);
return true;
}
private void showQuickAction(View view, int position)
{
RelativeLayout facsimile = (RelativeLayout) view.findViewById(R.id.lib_item_image_layout);
Log.i(LOGTAG, "Position:"+position);
currentPaper = TZReader.paperDao.load(libraryAdapter.getItemId(position));
Log.i(LOGTAG,"Set CurrentPaper:"+currentPaper.getTitelWithDate());
ActionItem downloadItem = new ActionItem(ACTION_ITEM_DOWNLOAD, "Download", getResources().getDrawable(R.drawable.ic_action_download));
ActionItem readItem = new ActionItem(ACTION_ITEM_READ, "Lesen", getResources().getDrawable(R.drawable.ic_action_read));
ActionItem deleteItem = new ActionItem(ACTION_ITEM_DELETE, "Löschen", getResources().getDrawable(R.drawable.ic_action_delete));
ActionItem cancelItem = new ActionItem(ACTION_ITEM_CANCEL, "Abbrechen", getResources().getDrawable(R.drawable.ic_action_cancel));
QuickAction mQuickAction = new QuickAction(this, QuickAction.HORIZONTAL);
switch (currentPaper.getState())
{
case Paper.DOWNLOADED_READABLE:
mQuickAction.addActionItem(readItem);
mQuickAction.addActionItem(deleteItem);
break;
case Paper.DOWNLOADED_BUT_UPDATE:
mQuickAction.addActionItem(downloadItem);
mQuickAction.addActionItem(deleteItem);
break;
case Paper.IS_DOWNLOADING:
mQuickAction.addActionItem(cancelItem);
break;
case Paper.NOT_DOWNLOADED:
mQuickAction.addActionItem(downloadItem);
break;
}
//Set listener for action item clicked
mQuickAction.setOnActionItemClickListener(new QuickAction.OnActionItemClickListener() {
#Override
public void onItemClick(QuickAction source, int pos, int actionId) {
//here we can filter which action item was clicked with pos or actionId parameter
switch(actionId)
{
case ACTION_ITEM_DOWNLOAD:
Intent downloadIntent = new Intent(getApplicationContext(), DownloadService.class);
downloadIntent.putExtra(DownloadService.PARAMETER_PAPER_DB_ID_LONG, currentPaper.getId());
startService(downloadIntent);
break;
case ACTION_ITEM_READ:
break;
case ACTION_ITEM_DELETE:
DeleteAlertDialogFragment newFragment = DeleteAlertDialogFragment.newInstance(currentPaper.getId());
newFragment.setStyle(SherlockDialogFragment.STYLE_NO_TITLE,0);
newFragment.show(getSupportFragmentManager(), "dialog");
break;
case ACTION_ITEM_CANCEL:
break;
}
}
});
mQuickAction.show(facsimile);
}
Maybe someone has any idea or hints for me how i can handle this problem!
Thanks a million in advance!
I found a solution for my problem.
The solution is that i implement my own ProgressBar, which contains now a BroadcastListerner and set the progress on each item. So i can change the value without need to call "notifydatasetchanged". perfect for my needs. Im still not sur if this is a good solution, but it works well.
Here is the code for the Progressbar:
public class ListeningProgressBar extends ProgressBar {
public ListeningProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
final IntentFilter intentDownloadProgressFilter = new IntentFilter(DownloadService.DOWNLOAD_PROGRESS_BROADCAST);
private long paperId = 0;
private boolean isReceiverRegistered = false;
#Override
protected void onAttachedToWindow() {
getContext().getApplicationContext().registerReceiver(downloadProgressBroadcastReceiver,intentDownloadProgressFilter);
isReceiverRegistered = true;
super.onAttachedToWindow();
}
#Override
protected void onDetachedFromWindow() {
if (isReceiverRegistered)
{
getContext().getApplicationContext().unregisterReceiver(downloadProgressBroadcastReceiver);
isReceiverRegistered = false;
}
super.onDetachedFromWindow();
}
private BroadcastReceiver downloadProgressBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
long id = intent.getLongExtra(DownloadService.PARAMETER_PAPER_DB_ID_LONG, -1);
int progressValue = intent.getIntExtra(DownloadService.PARAMETER_PAPER_PROGRESS_INT, 0);
if (paperId == id)
{
setProgress(progressValue);
}
}
};
public long getPaperId() {
return paperId;
}
public void setPaperId(long paperId) {
this.paperId = paperId;
}
}
I just use it as a normal custom view in my XML Layout.
In the Adapter i set the id of my content to give the receiver the data just to setprogress if its the right content.
At the end, my problem is solved, the progress is updated without the need to call notifydatasetchanged. Yeah!

Categories

Resources