Android execute after a view is complete - java

I am new to this, so go easy on me.
I am trying to execute a piece of code that needs to know the new width and height of an ImageView after the device is rotated.
The following code catches the configuration change, but the ImageView hasn't actually been changed yet, so I cannot get an accurate height and width.
// This fires whenever the configuration of the device changes (Portrait to Landscape of vice versa)
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
}
// This procedure moves the items based on the new size of the ImageView.
this.ShowMeasurements();
}
I have searched and looked at a number of ways that should work, but I am coming up short.
Any advice would be appreciated.

To be sure, that view has an accurate sizes you can try this code in onCreate():
imageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int viewWidth = imageView.getWidth();
int viewHeight = imageView.getHeight();
if (viewWidth > 0 && viewHeight > 0) {
//do some stuff here
}
//we remove this listener to not being notified every time the view
//changes it's size.
//But if you need this info every time, just remove the last line
imageView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
where, imageView is a reference to your ImageView

Related

toolbar is not hiding in android activity

i have an activity which contains a Video View from YouTube and we want that when someone rotate phone , toolbar should hide . actually we have rotation lock in our app , so no activity can get rotate except this video containing activity . and i am rotating screen using onConfigurationChange .
i have implmented custom toolbar in that activity and when configuration change and code changes position of screen it should get hide or show , but it is not working .
i am using this code to hide toolbar
getSupportActionbar.hide()
well , i have some doubts about this that why does it is not working :
1 . first when configuration changes , activity creates from the scratch so command for hide toolbar gets overwrite and shoes
2 . i saw somewhere that getSupportActionbar.hide() should come before , setContentView()
here is the code snippet of onConfigurationChange
#Override
public void onConfigurationChanged(Configuration newConfig) {
try {
super.onConfigurationChanged(newConfig);
int accelometerState = android.provider.Settings.System.getInt(getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0);
if (accelometerState == 1) {
if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
// toolbar.setVisibility(View.GONE);
// getSupportActionBar().hide();
isRotate = true;
}
if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
// toolbar.setVisibility(View.VISIBLE);
// getSupportActionBar().show();
isRotate = false;
}
} else {
if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
}
} catch (Exception ex) {
Log.e("home", "something went wrong in the VideoViewFullScreen.java");
}
}
i am trying to figure out what is the problem here ,
if anyone has some solution or suggestion i appreciate them .
Thank You in advance
Well in this case you can define a separate layout for the landscape mode using orientation qualifiers in the layout resource files. Learn more.

Using states to manage onTouchEvent

I am making a single screen application of a simple logic circuit. I am using onTouchEvent to handle user interactions. I am using ACTION_UP, ACTION_MOVE, & ACTION_DOWN right now, but this only allows me to use one gesture. I want to be able to selection an option in my UI, such as "AND" gate. I want to use one touch to select what I want to do, then the next touch to place the gate on the screen. Instead, my onTouchEvent only allows me to touch the component I want and I have to keep my finger on the screen to drag it to the location I want to place it. This is not want I want.
I've tried researching how to implement some sort of state variable to allow onTouchEvent to wait for the next touch, but I don't think I correctly understand how to implement it.
#Override
public boolean onTouchEvent(MotionEvent motionEvent) {
Log.d("Debugging", "In onTouchEvent");
if((motionEvent.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
Touch.horizontalTouched = (int)motionEvent.getX()/ grid.getBlockSize();
Touch.verticalTouched = (int)motionEvent.getY()/ grid.getBlockSize();
whatWasTouched = whatWasTouched(Touch.horizontalTouched, Touch.verticalTouched);
}else if((motionEvent.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_MOVE){
//do nothing, finger is moving on screen
}
else if((motionEvent.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP){
Touch.secondHorizontalTouch = (int)motionEvent.getX()/ grid.getBlockSize();
Touch.secondVerticalTouch = (int)motionEvent.getY()/ grid.getBlockSize();
placeComponent();
draw();
}
return true;
}
I expect my first touch on the screen to be able to select an option, ex: "AND", "OR", "NOT", "SWITCH", "EDIT", etc. and then my second touch completes the desired action. I also want to be able to touch a component I placed on the screen and then touch another component so I can wire them together.
By default, event listeners in Android are for waiting - you don't have to provide any delay.
Simply set the onTouchEvent(...) listener on the ImageView and show the first bitmap. When the ImageView is touched, show the next bitmap and so on. All you have to do is keep a count of how many touches there have been in order to know which image to show (image 1, 2, 3, 4 etc).
Example...
public class LoadImage extends Activity {
int imageNumber = 1;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_load_image);
//get an image and create a bitmap from it
ImageView imageView = (ImageView) findViewById(R.id.imageView);
imageView.setImageBitmap(bitmap);
}
#Override
public boolean onTouchEvent(MotionEvent evt) {
if (evt.getAction() == MotionEvent.ACTION_DOWN) {
imageNumber++;
switch (imageNumber) {
case 2:
// show image 2
break;
case 3:
// show image 3
break;
...
}
return true;
}
return false;
}
}
I was able to figure this out by using a static class that will handle the state of my touches.
#Override
public boolean onTouchEvent(MotionEvent motionEvent) {
Log.d("Debugging", "In onTouchEvent");
if (placeState.getState() == false) {
if ((motionEvent.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
Touch.horizontalTouched = (int) motionEvent.getX() / grid.getBlockSize();
Touch.verticalTouched = (int) motionEvent.getY() / grid.getBlockSize();
whatWasTouched = whatWasTouched(Touch.horizontalTouched, Touch.verticalTouched);
if(whatWasTouched.equals("DELETE")){
visualComponents.clear();
logicalComponents.clear();
}
placeState.toggleState();//sets to true
draw();
}
}else if (placeState.getState() == true) {
Touch.secondHorizontalTouch = (int) motionEvent.getX() / grid.getBlockSize();
Touch.secondVerticalTouch = (int) motionEvent.getY() / grid.getBlockSize();
placeComponent();
placeState.toggleState();//sets to false
draw();
}
return true;
}

OnClick in one RecyclerView item affects other items

Edit #1: Through debugging I've discovered that the bug 'disappears'. Basically I set a breakpoint and slowly go through steps of checking each multiChoiceItem and the heights of the other RecyclerView child items do not change. Does this mean it is a drawing/timing related issue?
Edit #2: Also, a new find, if I change the height of Child: 6 it changes for Child: 3 and Child: 0
I apologize for the long question. I've checked other answers regarding the same problem and none of them apply. I've tried solving this myself and just couldn't so I would love some help. If there is anything I can do to make this easier to read, please let me know and I'll get right on it!
With the way my code is written, this technically should be impossible to happen but yet here it is.
The Problem: I have an onClickListener() for a TextView within a RecyclerView item. The onClickListener() calls a multiChoiceItem AlertDialog in the container class of the RecyclerAdapter which then calls notifyDataSet(), after completed, with an addOnLayoutChangeListener() at the end which measures the height after the new RecyclerView is drawn.
Notifying that the data set ended then causes the TextView within the RecyclerView item to change to show the text of each Checked item. Then this height is measured in the addOnLayoutChangeListener() and sent to a ViewModel which measures the height of the same position item of three fragments and sets the items height to the max height so they all look the same height.
The Confusing Part: This problem only occurs for one of the three fragments AND the other effected item heights do not match the other two fragments. Which tells me that this is localized to one fragment (which has its own class)
The Code:
The code is long so I reduced it to what I think was important
The ViewHolder
class TextViewViewHolder extends RecyclerView.ViewHolder {
TextView vhTVTextView;
TextView vhTVMainTextView;
CardView vhTVCardView;
TextViewClickedListener vhTextViewClickedListener;
// Gets current position from 'onBindViewHolder'
int vhPosition = 0;
public TextViewViewHolder(View itemView, TextViewClickedListener textViewClickedListener) {
super(itemView);
this.vhTextViewClickedListener = textViewClickedListener;
this.vhTVCardView = itemView.findViewById(R.id.thoughtCard);
this.vhTVTextView = itemView.findViewById(R.id.thoughtNumber);
this.vhTVMainTextView = itemView.findViewById(R.id.textEntry);
/*
When the main TextView is clicked, it calls a function in the container
'FragTextView' which pops up an AlertDialog. It was chosen to do it in the
container instead of here because the Adapter is so adapt the lists data to the view
and the container is what dictates what the lists data actually is.
*/
vhTVMainTextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(vhTextViewClickedListener != null) {
vhTextViewClickedListener.onTextViewClicked(vhPosition);
}
}
});
}
}
onBindViewHolder
#Override
public int getItemViewType(int position) {
/*
If mThoughtEntries is not null, then that means we can find the ViewType we are working
with inside of it. Otherwise, we are mDistortions and we must be working on TYPE_TEXTVIEW
*/
if(mThoughtEntries != null) return mThoughtEntries.get(position).getViewType();
else return Constants.TYPE_TEXTVIEW;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
int adapterPosition = holder.getAdapterPosition();
switch (holder.getItemViewType()) {
case Constants.TYPE_EDITTEXT:
EditTextViewHolder editTextViewHolder = (EditTextViewHolder)holder;
// update MyCustomEditTextListener every time we bind a new item
// so that it knows what item in mDataset to update
editTextViewHolder.mMyCustomEditTextListener.setTWPosition(holder.getAdapterPosition());
//Displaying list item to its correct position
editTextViewHolder.vhETTextView.setText(String.valueOf(adapterPosition + 1));
editTextViewHolder.vhETEditText.setText(mThoughtEntries.get(adapterPosition).getThought());
break;
case Constants.TYPE_TEXTVIEW:
TextViewViewHolder textViewViewHolder = (TextViewViewHolder)holder;
// Send current position to viewHolder so when the text listener is called, it knows
// exactly which position of the Distortions list to change
textViewViewHolder.vhPosition = adapterPosition;
//Displaying list item to its correct position
textViewViewHolder.vhTVTextView.setText(String.valueOf(adapterPosition + 1));
textViewViewHolder.vhTVMainTextView.setText(distortionsToString(mDistortions.get(adapterPosition)));
break;
}
}
AlertDialog in Parent
#Override
public void onTextViewClicked(int position) {
//pass the 'context' here
AlertDialog.Builder alertDialog = new AlertDialog.Builder(getContext());
final int recyclerPosition = position;
/*
Turning the distortions into a list of strings and an array of what should, or should
not, be checked.
*/
final String[] distortionStrings = distortionNameToStringArray(mDistortions.get(position));
final boolean[] checkedDistortions = distortionCheckToBooleanArray(mDistortions.get(position));
alertDialog.setMultiChoiceItems(distortionStrings, checkedDistortions,
new DialogInterface.OnMultiChoiceClickListener() {
#Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
if (isChecked) {
// If the user checked the item, add it to the selected items
mDistortions.get(recyclerPosition).get(which).setChecked(true);
} else {
// Else, if the item is already in the array, remove it
mDistortions.get(recyclerPosition).get(which).setChecked(false);
}
/*
Because the RecyclerView takes a while to draw, if we call the below function
as we normally we would, it would appear to have no effect because it would
be automatically overwritten when the RecyclerView is drawn. So we call this
onLayout change listener to wait til the view is drawn and then we call
the function
*/
mRecyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
mRecyclerView.removeOnLayoutChangeListener(this);
// Send new height to the ViewModel
if(mLayoutManager.findViewByPosition(recyclerPosition) != null) {
// Get view of item measuring
View recyclerChild = mLayoutManager.findViewByPosition(recyclerPosition);
// Get LinearLayout from view
LinearLayout linearLayout = recyclerChild.findViewById(R.id.horizontalLayout);
// This is called to find out how big a view should be. The constraints are to check
// measurement when it is set to 'wrap_content'.
linearLayout.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
// Get height of the specified view
int height = linearLayout.getMeasuredHeight();
// Send to child abstracted class which then calls function from 'SharedEntryFragments'
setViewModelHeight(height, recyclerPosition);
}
}
});
mAdapter.notifyDataSetChanged();
}
});
alertDialog.setPositiveButton("Okay", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// DO SOMETHING HERE
dialog.cancel();
}
});
AlertDialog dialog = alertDialog.create();
dialog.show();
}
The function that makes all the fragment item heights equal
I know this part of the code doesn't affect it because where the views that heights are changed are skipped by if(positionalHeight.get(i) != 0) {} So technically...they should never change!
/*
This is the listener that will set all the RecyclerViews childrens heights. It
listens to getTallestLiveHeight() inside of 'SharedEntryFragments.java' and when
a change occurs, this is called
*/
if(getActivity() != null) {
// The container holds the ViewModel so this must make sure getActivity() is not null
mViewModel = ViewModelProviders.of(getActivity()).get(SharedEntryFragments.class);
/*
Creates the observer which updates the UI. The observer takes the
PositionalHeight class as an input. This class keeps track of which index
of the RecyclerView to change and what height it will be changed to.
*/
final Observer<List<Integer>> maxHeight = new Observer<List<Integer>>() {
#Override
public void onChanged(#Nullable final List<Integer> positionalHeight) {
if (positionalHeight != null) {
// Get the index that we are going to change and its height
//int position = positionalHeight.getPosition();
//int height = positionalHeight.getHeight();
/*
We're going to run through each child of mRecyclerView and change
its height accordingly
*/
int listSize = positionalHeight.size();
for(int i = 0; i < listSize; i++) {
// If height reads zero then skip because it will make our view disappear
if(positionalHeight.get(i) != 0) {
// This is the child item that we will be changing
View recyclerChild = mLayoutManager.findViewByPosition(i);
// Ensure that the child exists before continuing
if (recyclerChild != null) {
// We will be changing the CardView's height
// TODO might have to add a check to detect which viewholder
CardView cardView = recyclerChild.findViewById(R.id.thoughtCard);
// Get the LayoutParams first to ensure everything stays the same
ViewGroup.LayoutParams lparams = cardView.getLayoutParams();
// Get and set height
lparams.height = positionalHeight.get(i);
cardView.setLayoutParams(lparams);
}
}
}
}
}
};
mViewModel.getTallestLiveHeight().observe(this, maxHeight);
}
}
I wish I could provide a better answer for other people but this is what I discovered:
For some reason when I call mAdapter.notifyDataSetChanged(); in the AlertDialog function, every third item in the RecyclerView changed to the equaled height. I decided to change it to mAdapter.notifyItemChanged(recyclerPosition); to save on memory and, coincidentally, the bug has disappeared.
If someone could explain why, I will set that as the accepted answer but as of now, this satisfies the question so I will keep it as an answer.

How to change width of ImageView in runtime?

want to turn a card in my app. I have 2 ImageViews 1 Cardfront and 2 Cardback.
My theory ->
I change the width of the cardfront from 100% to 0%
I change the width of the cardback from 0% to 100%
I googled for solution on changing the width of am ImageView while runtime, but the solution I found don't work.
// Code for step 1
ViewGroup.LayoutParams params = ivBomb.getLayoutParams();
int width = params.width;
for (int i = 1; i<width; i++) {
params.width--;
ivBomb.setLayoutParams(params);
Thread.sleep(10); // To see the change
}
When I start it without the Thread.sleep(10);, it disappears instantly. But when I start it with the Thread.sleep(10);, it waits ~7s and then disappears instantly.
What am I doing wrong?
You can use animation to get the flip card effect. This card flip animation tutorial here is for a fragment, but you can use the same animations for your views like
Animation cardFlipRightOut = AnimationUtils.loadAnimation(this, R.anim.card_flip_right_out);
cardFlipRightOut.setAnimationListener(new Animation.AnimationListener(){
#Override
public void onAnimationStart(Animation arg0) {
}
#Override
public void onAnimationRepeat(Animation arg0) {
}
#Override
public void onAnimationEnd(Animation arg0) {
Animation cardFlipLeftIn = AnimationUtils.loadAnimation(this, R.anim.card_flip_left_in);
cardFrontView.startAnimation(cardFlipLeftIn);
}
});
cardBackView.startAnimation(cardFlipRightIn);
Try calling one of these after setLayoutParams() :
1. requestLayout()
2. invalidate()
Since, requestLayout() should be called, when view's position or bounds in the parent layout have been changed, while invalidate() should be called when view's appearance has been changed. When you call requestLayout() then onLayout() and onMeasure() methods of the view will be fired, on the other hand when you call invalidate(), then onDraw() method will be fired.

Override single tap of Photoview

I have an ImageView inside of a view pager with an ActionBar at the top. I would like to be able to single tap to hide the action bar, and I would also like to be able to pinch zoom and pan on each ImageView.
To implement the single tap to hide the action bar I have a simple OnClickListener that hides it.
To implement the pinch zoom and pan on each ImageView I am using the PhotoView Library Project.
I am having issues because only one touch event listener can be associated with an ImageView, and the implementing the PhotoView Library project overwrites my OnClickListener to hide the ActionBar with,
parent.requestDisallowInterceptTouchEvent(true);
I am not sure how to go about getting both implemented at the same time. It seems like the only solution is to create my own Pinch Zoom ImageView in order to control touch events myself.
Found out that the PhotoView library actually allows me to set onViewTap for the PhotoViewAttacher object which is exactly what I wanted.
To create the PhotoViewAttacher in the current Fragment/Activity have it implement PhotoViewAttacher.OnViewTapListener, create the attacher,
PhotoViewAttacher mAttacher = new PhotoViewAttacher(imageView);
mAttacher.setOnViewTapListener(this);
and add the following function,
public void onViewTap(View view, float x, float y) {
// your code here
}
Source
You'll have to override the PhotoView library itself. If you look at the source code, the PhotoViewAttacher class is the one that handles the onTouch events.
You'll have to add the special funcionality you're looking for at this part of the code (specially, the ACTION_DOWN) event:
#Override
public final boolean onTouch(View v, MotionEvent ev) {
boolean handled = false;
if (mZoomEnabled && hasDrawable((ImageView) v)) {
ViewParent parent = v.getParent();
switch (ev.getAction()) {
case ACTION_DOWN:
// First, disable the Parent from intercepting the touch
// event
if (null != parent)
parent.requestDisallowInterceptTouchEvent(true);
else
Log.i(LOG_TAG, "onTouch getParent() returned null");
// If we're flinging, and the user presses down, cancel
// fling
cancelFling();
break;
case ACTION_CANCEL:
case ACTION_UP:
// If the user has zoomed less than min scale, zoom back
// to min scale
if (getScale() < mMinScale) {
RectF rect = getDisplayRect();
if (null != rect) {
v.post(new AnimatedZoomRunnable(getScale(), mMinScale,
rect.centerX(), rect.centerY()));
handled = true;
}
}
break;
}
// Check to see if the user double tapped
if (null != mGestureDetector && mGestureDetector.onTouchEvent(ev)) {
handled = true;
}
if (!handled && null != parent) {
parent.requestDisallowInterceptTouchEvent(false);
}
// Finally, try the Scale/Drag detector
if (null != mScaleDragDetector
&& mScaleDragDetector.onTouchEvent(ev)) {
handled = true;
}
}
return handled;
}

Categories

Resources