Android list item reuse - android

I needed TextView which would flash on text change - so I made CustomTextView like described in here. It works great but I have problem when I set that CustomTextView in list item. Because of ListView items are being reused my CustomTextView keeps flashing when scrolling up/down because it's context changes and now it 'points' to another list item.
Problem is I don't know how to determine when context of the item is changed, so I cannot put noFlash flag (Text property of the CustomTextView is not set to null, so I cannot use that either)

On the end I had to make custom adapter with some code in BindBindbleCode
protected override void BindBindableView(object source, IMvxListItemView viewToUse)
{
var newValue = source as ListItemVM;
var oldValue = viewToUse.DataContext as ListItemVM;
if (newValue.ItemID != oldValue.ItemID)
newValue.Rebinded = true;
base.BindBindableView(source, viewToUse);
}
then I added NotLoaded property on CustomTextView and binded those two together. So when Rebinded is true it sets NotLoaded to true signalizing that data in that TextView is not loaded. If data is not loaded there's no need to flash background of CustomTextView.
public string AnimatingText
{
get { return Text; }
set
{
if (Text == value)
return;
if (NotLoaded)
{
Text = value;
NotLoaded = false;
return;
}
Text = value;
// Do your animation here
}
}

Related

Toggling a TextView visibility on click

I want to toggle textView on click.
val targetView = findViewById<TextView>(R.id.targetText)
targetView.text = 'example'
targetView.setOnClickListener {
if (it.visibility == View.VISIBLE) {
it.visibility = View.INVISIBLE
} else {
it.visibility = View.VISIBLE
}
}
But once I clicked and visibility of text was changed to invisible, then I can not click this again.
Suggestion: How about creating a view behind the text and make use of the view for toggle triggering instead of text.
i.e. onViewClicked { toggleTextVisibility() }
Store the text of the textview in a temporary string. Instead of toggling the visibility, set the text to null and then back to what it was using the temp variable.
String final tempText = "your text"
then
targetView.setOnClickListener(new View.OnClickListener) {
#Override
public void onClick(View view){
if (view.getText() == null) {
view.setText(tempText);
} else {
view.setText(" ");
}
});
}
just make sure your textview on the layout properties has a set minWidth and not wrap_content as it will disappear when you set the text to null

textView change to shorter text dynamically instead of ellipsize

I have this code:
final ViewTreeObserver[] viewTreeObserver = {myAcco
viewTreeObserver[0].addOnPreDrawListener(
new OnPreDrawListener() {
#Override
public boolean onPreDraw() {
int chipWidth =
myAccountView.getMeasuredWidth()
- myAccountView.getPaddingLeft()
- myAccountView.getPaddingRight();
if (chipWidth > 0) {
myAccountView.setText(
setChipTextWithCorrectLength(
getContext().getString(R.string.og_my_account_desc_long_length),
getContext().getString(R.string.og_my_account_desc_meduim_length),
getContext().getString(R.string.og_my_account_desc_short_length),
chipWidth));
viewTreeObserver[0] = myAccountView.getViewTreeObserver();
if (viewTreeObserver[0].isAlive()) {
viewTreeObserver[0].removeOnPreDrawListener(this);
}
}
return true;
}
});
}
#VisibleForTesting
String setChipTextWithCorrectLength(
String longDesc, String mediumDesc, String shortDesc, int clipWidth) {
if (!isTextEllipsized(longDesc, clipWidth)) {
return longDesc;
}
if (!isTextEllipsized(mediumDesc, clipWidth)) {
return mediumDesc;
}
return shortDesc;
}
private boolean isTextEllipsized(String text, int clipWidth) {
TextPaint textPaint = myAccountView.getPaint();
Toast.makeText(this.getContext(), "textPaint.measureText(text) = "+textPaint.measureText(text)+" clipWidth ="+ clipWidth,
Toast.LENGTH_LONG).show();
return textPaint.measureText(text) > clipWidth;
}
The code should change the text in a textView dynamically when ellipsize is needed.
However, when i run the application I see sometimes the view (clip) width == the long text width and there is still ellipsize in the UI.
I see that happens when the textView is not visible and toggeled to be visible.
Should I calculate the vlip (view) width differently?
Any special way to measure textView before becomes visible?
You should check with
getEllipsisCount()
to see the source of the problem.
Definition : Returns the number of characters to be ellipsized away, or 0 if no ellipsis is to take place.
Yes you can use textView.getLayout().getEllipsisStart(0) ,make sure your textView is singleline , you can do this by adding android:maxLines="1" to your texView. It return the index from which the text gets truncated else returns 0.
Depending on your observation , it might be that the view is not recalculating after changing the visibility to visible again, try to use textView.setVisibility(View.GONE) instead of textView.setVisibility(View.INVISIBLE); .This might cause the preDraw() to be called again.
You might also consider using addOnGlobalLayoutListener() to keep the track of visibilty changes of your view, for reference.

Anko ListItem setOnClickListener

I'm trying to play around with some Kotlin and Anko (more familiar with iOS) and taking from their example, there is this code:
internal open class TextListWithCheckboxItem(val text: String = "") : ListItem {
protected inline fun createTextView(ui: AnkoContext<ListItemAdapter>, init: TextView.() -> Unit) = ui.apply {
textView {
id = android.R.id.text1
text = "Text list item" // default text (for the preview)
isClickable = true
setOnClickListener {
Log.d("test", "message")
}
init()
}
checkBox {
id = View.generateViewId()
setOnClickListener {
Log.d("hi", "bye")
}
init()
}
}.view
My row appears how I want with a checkbox and textview. But I want to bind an action to the row selection not the checkbox selection. Putting a log message in both, I see that I get a log message when the row is selected which flips the checkbox. It does not, however, log my "test:message" from the textView click handler. Is there a way to get around this?
Apparently your issue has been addressed here. As the checkbox is consuming all the focus of ListItem you should set the CheckBox's focusable flag to false:
checkBox {
focusable = View.NOT_FOCUSABLE
}
Unfortunately setFocusable call requires at least API 26, but you could define view .xml and inflate the view manually as described here:
<CheckBox
...
android:focusable="false" />
Alternatively you could try setting a onTouchListener returning false which means the touch event will be passed to underlying views.
Let me know if it works ;)

addView inconsistent behavior

I have a custom component that extends RelativeLayout which in turns holds a GridLayout(named mFormLayout). I have a public method that adds two spinners with their proper adapter source and an imageview which acts as a button to remove rows.
public class EditableTwinSpinnerGridForm extends EditableGridForm
{
public void addTwinSpinnerRow(final Locale.MapKey spinner1DefVal, final Locale.MapKey spinner2DefVal)
{
Spinner spinner1 = createSpinner(mTSP.getFirstSpinnerRes(), spinner1DefVal.getId());
spinner1.setOnItemSelectedListener(mTSP.getIsl());
Spinner spinner2 = createSpinner(mTSP.getSecondSpinnerRes(), spinner2DefVal.getId());
ImageView rmvBtn = createRemoveBtn();
mFormLayout.addView(spinner1);
mFormLayout.addView(spinner2);
mFormLayout.addView(rmvBtn);
}
}
For some reason, this method works when I am adding rows from a call to onCreate in an activity, but when I am calling this method after the activity is created(from an onclicklistener) the Spinners are either not there or only one of them shows up. They do take the space because I see the row and the removable image view.
I have also noticed that when I focus on a EditText in the same activity and the keyboard pops up, the added spinners show up when I press back to remove the keyboard.
Here's the code I use to create a spinner :
protected Spinner createSpinner(Integer spinnerSrc, String defaultSpinnerValue)
{
Spinner spinner = new Spinner(mCtx, Spinner.MODE_DIALOG);
// Setting the bg color to the containing color to remove the spinner arrow.
spinner.setBackgroundResource(R.color.container_bg);
SparseArray<Phrase> map = Locale.getInstance().getMap(spinnerSrc);
PhraseArrayAdapter adapter = createSpinnerFromMap(spinnerSrc);
spinner.setAdapter(adapter);
if (defaultSpinnerValue.equals(Utilities.EMPTY_STRING) || defaultSpinnerValue.isEmpty())
{
throw new IllegalStateException();
}
else
{
Utilities.getInstance().setMapSpinnerPosByValue(map, defaultSpinnerValue, spinner);
}
adapter.setDropDownViewResource(R.layout.editable_spinner_dropdown_item);
setSpinnerLayoutParams(spinner);
return spinner;
}
protected void setSpinnerLayoutParams(Spinner spinner)
{
GridLayout.LayoutParams lp = createDefaultGridParams();
lp.setGravity(Gravity.FILL_HORIZONTAL);
lp.width = 250;
lp.rightMargin = 0;
spinner.setLayoutParams(lp);
}
The code works when the activity is loaded so I'm a bit stumped. I looked around and some people suggested I set LayoutParams in addView, but why would this method work in onCreate, but not afterwards?
Here's what's happening visually(The first three rows are added from a loop in onCreate(), the two second ones are added by pressing "Add +"). As you can see the second spinner isn't showing up, sometimes both aren't showing up. I also tried calling invalidate and requestLayout to no avail.
I had looked into the invalidate method, here's where it is located currently :
public abstract class EditableForm extends RelativeLayout implements ObservableInt
{
private class OnAddClicked implements OnClickListener
{
#Override
public void onClick(View v)
{
onAddClicked(v);
EditableForm.this.invalidate();
mFormLayout.invalidate();
}
}
}
Which calls(In a subclass of EditableGrid) :
#Override
protected void onAddClicked(View clickedView)
{
addTwinSpinnerRow();
notifyObservers(new ObservableData(EDITABLE_ADD_GRID_CLICKED, null));
}
protected void addTwinSpinnerRow()
{
Locale.MapKey v1 = Locale.getInstance().getMap(mTSP.getFirstSpinnerRes()).get(0).getMapId();
Locale.MapKey v2 = Locale.getInstance().getMap(mTSP.getSecondSpinnerRes()).get(0).getMapId();
addTwinSpinnerRow(v1, v2);
}
Have you tried calling the Invalidate method of the container view rather than the added view?
Most likely the views you are adding are there, they just need to be drawn which is suggested by your keyboard hide/show difference. Does rotating the device also cause them to appear? If so, this again suggests that you need to redraw your custom layout.
When it's necessary to execute invalidate() on a View?

Update parent of TextSwitcher when text changed

Im using a textSwither for hide/show a part of text like this way :
txtDescription.setInAnimation(in);
txtDescription.setOutAnimation(out);
txtDescription.setFactory(new ViewSwitcher.ViewFactory() {
#Override
public View makeView() {
TextView textView = new TextView(ShopContentDetailActivity.this);
textView.setTextColor(getResources().getColor(R.color.NightDark));
return textView;
}
});
And call this method for change text :
private void changeDescription() {
if (displayShort)
txtDescription.setText(Html.fromHtml(shortDescription));
else txtDescription.setText(fullDescription);
displayShort = !displayShort;
}
My problem is when user touch textSwitcher to display text and touch again to hide it ,the height of parent view (a linear layout ) of textSwitcher remain as when text is displayed and dont change its height.
How can i update the parent view when text change ? (its ok when touch for display full text)
I resolve my problem in this way : (I think TextSwutcher change visibility of TextViews to INVISIBLE not GONE then i use this trck)
private void changeDescription() {
if (displayShort) {
txtDescription.setCurrentText(null); // this line change invisible textView visible but without text and make it height 0 and then set the second textView text
txtDescription.setText(Html.fromHtml(shortDescription));
} else txtDescription.setText(shopContent.getDescription());
displayShort = !displayShort;
}
But there is a problem that is not so important for my case: when you hide a part of text its blink.
if i resolve it i will update my answer.

Categories

Resources