I am using https://github.com/florent37/TutoShowcase this showcaseview library in my code.
It works fine in activity and fragment.But when I call in recyclerview item it shows multiple popups and gets blackout.
mRecyclerViewList.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Logger.log("Call");
TextView textView = (TextView) mRecyclerViewList.getChildAt(0).findViewById(R.id.txt_add_tocart_btn);
Logger.log("Textview" + textView);
textView.setFocusableInTouchMode(true);
TutoShowcase.from((Activity) context).setContentView(R.layout.tuto_showcase_tuto_sample)
.setFitsSystemWindows(true).on(textView).addRoundRect(35).showOnce("1").show();
// unregister listener (this is important)
if (Build.VERSION.SDK_INT < 16) {
mRecyclerViewList.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
mRecyclerViewList.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
}
});
How can I avoid multiple popup's?
Your question how to avoid Multiple popup's:
Just set a boolean value to avoid showing multiple times.
boolean isShown = false;
mRecyclerViewList.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Logger.log("Call");
if(!isShown){
TextView textView = (TextView) mRecyclerViewList.getChildAt(0).findViewById(R.id.txt_add_tocart_btn);
Logger.log("Textview" + textView);
textView.setFocusableInTouchMode(true);
TutoShowcase.from((Activity) context).setContentView(R.layout.tuto_showcase_tuto_sample)
.setFitsSystemWindows(true).on(textView).addRoundRect(35).showOnce("1").show();
isShown = true;
}
// unregister listener (this is important)
if (Build.VERSION.SDK_INT < 16) {
mRecyclerViewList.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
mRecyclerViewList.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
}
});
As described in documentation of OnGlobalLayoutListener:
to be invoked when the global layout state or the visibility of views within the view tree changes.
That's why you got many and many showcase views. Every time ViewTree changed, you generate showcase.
You don't need for ViewTreeObserver and GlobalLayoutListener.
Move
TutoShowcase.from
to onViewCreated for example.
Related
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?
I am a new in android programming, I made a layout with this figure:
Now I want to know when one of these buttons clicked I should run an new activity or change visibility to false and show new layout without run a new activity, what is the best solution?
You consider that count of these buttons are more than ten.
I want show a text with image,..(when clicked) because that is a educational book and these buttons are chapters list of that book
for an example if you want to change only the layout then you could do something like this
FirstButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View v) {
FirstView();
}
});
/
void FirstView(){
setContentView(R.layout.yourOtherLayout);
// then declare the layout views here.
firstView=false;
}
you can do this in all the buttons just create different methods for each
to handle the Back Button you can declare Boolean variables and use If else Statement to loop through them for example
boolean firstView = true, secondView = true;
#Override
public void onBackPressed(){
if (firstView == false ){
then firstView Is Showing.
// show the view you want and set
firstView = true;
}else if (SO ON)...
else { super.OnBackPressed(); // exit }
}
So my activity uses the PageAdapter and ViewPager correctly, where 3 images can be scrolled from left to right. So then I have left and right arrows on the activity and I set an onClickListener for both images to scroll left and right with ViewPager pager. The method is like
private void setOnClickArrows(final ViewPager pager){
ImageView rightArrow = (ImageView) view.findViewById(R.id.right);
ImageView leftArrow = (ImageView) view.findViewById(R.id.left);
rightArrow.setOnClickListener(new OnClickListener ()
#Override
public void onClick(View v) {
pager.setCurrentItem(pager.getCurrentItem() + 1);
}
});
leftArrow.setOnClickListener(new OnClickListener ()
#Override
public void onClick(View v) {
pager.setCurrentItem(pager.getCurrentItem() - 1);
}
});
}
This works perfectly. However, I was wondering how the ViewPager checks if the index is out of bounds and if I should do it?
Yes, it actually does. Refer to the source and have a look at the setCurrentItemInternal method (called by setCurrentItem()).
There are a few checks in place to avoid a crash, and among one of them is this, which avoids OOBExceptions:
if (item < 0) {
item = 0;
} else if (item >= mAdapter.getCount()) {
item = mAdapter.getCount() - 1;
}
Should you check yourself? Up to you. I wouldn't because those checks are trivial, so I wouldn't expect the checks to disappear.
What my application first does is it loads ListView whose items have invisible CheckBoxes by setting its visibility View.Gone. When the user tabs a menu button then it will turn on and off the CheckBox visibility and some other layouts. Below is the code, I removed some unnecessary parts:
private void editmodeSwitch(boolean flag){
// get topbar, bottombar, and bottombar2
LinearLayout topbar = (LinearLayout) findViewById(R.id.task_topbar_linearLayout);
LinearLayout bottombar = (LinearLayout) findViewById(R.id.task_bottombar1_linearlayout);
LinearLayout bottombar2 = (LinearLayout) findViewById(R.id.task_bottombar2_linearlayout);
if(flag){
isEditmodeOn = true;
// make topbar and bottombar2 visilble, but bottombar gone
topbar.setVisibility(View.VISIBLE);
bottombar.setVisibility(View.GONE);
bottombar2.setVisibility(View.VISIBLE);
// make checkboxes visible in listview visible as well
for(int i=0; i<listView.getChildCount(); i++){
LinearLayout ll = (LinearLayout) listView.getChildAt(i);
CheckBox cb = (CheckBox) ll.findViewById(R.id.task_row_checkBox1);
cb.setVisibility(View.VISIBLE);
}
}
else{
isEditmodeOn = false;
topbar.setVisibility(View.GONE);
bottombar.setVisibility(View.VISIBLE);
bottombar2.setVisibility(View.GONE);
// set each checkbox false and its visibility gone
for(int i=0; i<listView.getChildCount(); i++){
LinearLayout ll = (LinearLayout) listView.getChildAt(i);
CheckBox cb = (CheckBox) ll.findViewById(R.id.task_row_checkBox1);
cb.setVisibility(View.GONE);
cb.setChecked(false);
}
}
}
It works fine but the problem is the application doesn't work when the screen rotates(changes the screen orientation). Everything worked fine as it displayed some layouts but only CheckBoxes in list items. Below is the code inonCreate()`:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.task_layout);
initialize();
loadDB();
updateListAdapter(list_title, list_date);
// in case of screen rotation
if(savedInstanceState != null){
isEditmodeOn = savedInstanceState.getBoolean(EDITMODE_CHECK);
isItemChecked = savedInstanceState.getBoolean(ITEM_CHECK);
if(isEditmodeOn){
if(!isItemChecked){
Log.i(tag, "item NOT checked");
editmodeSwitch(true);
} else{
//this is something different so please don't mind
deditmodeSwitch(savedInstanceState.getBooleanArray(LIST_CB_CHECK));
}
}
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// save values for rotation
outState.putBoolean(EDITMODE_CHECK, isEditmodeOn);
outState.putBoolean(ITEM_CHECK, isItemChecked);
outState.putBooleanArray(LIST_CB_CHECK, list_cb_check);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.i(tag, "you're in onRestoreInstanceState()");
// in case of screen rotation
if(savedInstanceState != null){
isEditmodeOn = savedInstanceState.getBoolean(EDITMODE_CHECK);
isItemChecked = savedInstanceState.getBoolean(ITEM_CHECK);
if(isEditmodeOn){
if(!isItemChecked){
Log.i(tag, "item NOT checked");
editmodeSwitch(true);
} else{
// this is for something else so please ignore this part
editmodeSwitch(savedInstanceState.getBooleanArray(LIST_CB_CHECK));
}
}
}
What I guessed is the ListView is being loaded at the end. Therefore, even if the code in onCreate() makes CheckBoxes visible, the CheckBoxes will become invisible again as its initialization in xml will do so. However, I'm stuck here and need your advice to solve this problem. Can anyone help me?
Just in case, below is the checkbox code of layout xml file for getview.
<CheckBox android:id="#+id/task_row_checkBox1" android:gravity="right"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:visibility="gone"
/>
Override onSaveInstanceState for saving value on screen rotation and onRestoreInstanceState as:
protected void onCreate(Bundle savedInstanceState) {
if(null != savedInstanceState)
{
Boolean IntTest = savedInstanceState.getBoolean("ITEM_CHECK");
Boolean StrTest = savedInstanceState.getBoolean("ITEM_CHECK");
Log.e(TAG, "onCreate get the savedInstanceState+IntTest="+IntTest+"+StrTest="+StrTest);
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save away the CheckBoxes states, so we still have it if the activity
// needs to be killed while paused.
savedInstanceState.putBoolean(EDITMODE_CHECK, 0);
savedInstanceState.putBoolean(ITEM_CHECK, 0);
super.onSaveInstanceState(savedInstanceState);
Log.e(TAG, "onSaveInstanceState");
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Boolean IntTest = savedInstanceState.getBoolean(EDITMODE_CHECK);
Boolean StrTest = savedInstanceState.getBoolean(ITEM_CHECK);
Log.e(TAG, "onRestoreInstanceState+IntTest="+IntTest+"+StrTest="+StrTest);
}
Similar to how you override onCreate, you can override onConfigurationChanged(...) which you can setup to run when the screen changes orientation.
In order for OnConfigurationChanged(...) to be trigger when the screen rotates, you need to to edit your manifest and put that relationship/rule in.
It's easy to do but takes a bit of explaining and it was answered before in this question:
Activity restart on rotation Android
Edit: Here is the dev guide on how to handle configuration changes
http://developer.android.com/guide/topics/resources/runtime-changes.html
Edit #2: First, let me suggest using Imran's solution. It follows the Developer Guide better and the end results will be the same.
Now, for the onConfigurationChanged solution.
Look at what you are doing with your onCreate:
1) Set the view. (Checkboxes are hidden at this point. Right?)
2) Call your DB and determine if you should display checkboxes (edit mode)
3) Make all the checkboxes visible.
Now, onConfigurationChanged also calls setContentView, at which point all your checkboxes are hidden again. So you need to repeat the process of making your checkboxes visible (#3 above). You probably don't need to repeat step #2 because the value should be retained, but I'm not sure how the logic of your app works, so you may need to re-do step #2.
Does that make sense?
Based on my experience, getview seems to be triggered at the end and it was why 'onRestoreInstanceState()' and 'onConfigurationChanged()' could not make it as getview will reset my checkboxes invisible as initialization in the layout xml file.
Therefore, the only solution I could find out was I must control them in getview for the answer.
I want to make a TextView that is collapsable by user's touch.
When the TextView collapsed, I set textView.setMaxLines(4);.
How to I clear this state in my expand method?
I can only think of call setMaxLines() with a value large number like 10000.
Are there better ways to implement this?
Actually, the way android platform does that is by setting the MaxLine to Integer.MAX_VALUE.
textView.setMaxLines(Integer.MAX_VALUE);
also, if you are using Ellipsize, don't forget to set to null.
textView.setEllipsize(null);
just check how the android framework do just that ;) watch the setMaxLines(Integer.MAX_VALUE);
private void applySingleLine(boolean singleLine, boolean applyTransformation) {
mSingleLine = singleLine;
if (singleLine) {
setLines(1);
setHorizontallyScrolling(true);
if (applyTransformation) {
setTransformationMethod(SingleLineTransformationMethod.getInstance());
}
} else {
setMaxLines(Integer.MAX_VALUE);
setHorizontallyScrolling(false);
if (applyTransformation) {
setTransformationMethod(null);
}
}
}
You can find this in the source code of Android Open Source Project (AOSP)
https://source.android.com/source/downloading
If you do not want to download the source, you can view the source on a mirror like this one at github.
https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/java/android/widget/TextView.java
Try this (infoView.getLineCount()):
public void onMoreClick(View v) {
Button btn = (Button) v;
if(!moreSwitcher) {
infoView.setMaxLines(infoView.getLineCount());
infoView.setLines(infoView.getLineCount());
moreSwitcher = true;
btn.setText(R.string.collapse);
}else{
infoView.setMaxLines(5);
infoView.setLines(5);
moreSwitcher = false;
btn.setText(R.string.expand);
}
}