I have a HorizontalScrollView which contains a LinearLayout to hold all my view. I add about 20 RelativeLayout which contains a ImageView and TextView to the LinearLayout. I would like to only load images if the ImageView is on the screen (as I scroll to the ImageView).
I tried following this post to use getHitRect(), on the thumbnail, however, the Rect (bounds) for the thumbnail is always 0, 0-0, 0, resulting in my method to return false. What am I doing wrong?
thumbnailLayout is my LinearLayout inside the HorizontalScrollView
thumbnailScroll is my HorizontalScrollView
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.e(TAG, "running");
for (int i = 0; i < thumbnailLayout.getChildCount(); i++) {
RelativeLayout view = (RelativeLayout) thumbnailLayout.getChildAt(i);
ImageView thumbnail = (ImageView) view.findViewById(R.id.thumbnail);
if (thumbnailIsOnScreen(thumbnail)) {
Log.e(TAG, items.get(i).getTitle() + " has downloaded");
app.setImage(items.get(i).getThumbnailSmall(), thumbnail);
}
}
}
});
private boolean thumbnailIsOnScreen(ImageView thumbnail) {
Rect bounds = new Rect();
thumbnail.getHitRect(bounds);
Rect scrollBounds = new Rect(thumbnailScroll.getScrollX(), thumbnailScroll.getScrollY(),
thumbnailScroll.getScrollX() + thumbnailScroll.getWidth(), thumbnailScroll.getScrollY()
+ thumbnailScroll.getHeight());
return Rect.intersects(scrollBounds, bounds);
}
Edit
I'm tred using TreeObserver and found that my method to check if the thumbails are on screen is wrong. It still grabs all the images, and constantly loops (because I'm using onPreDrawListener?)
thumbnailLayout.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
#Override
public boolean onPreDraw() {
Log.e(TAG, "running");
for (int i = 0; i < thumbnailLayout.getChildCount(); i++) {
RelativeLayout view = (RelativeLayout) thumbnailLayout.getChildAt(i);
ImageView thumbnail = (ImageView) view.findViewById(R.id.thumbnail);
if (thumbnailIsOnScreen(thumbnail)) {
Log.e(TAG, items.get(i).getTitle() + " has downloaded");
app.setImage(items.get(i).getThumbnailSmall(), thumbnail);
}
}
return true;
}
});
Have you thought about using ViewPager?
http://developer.android.com/reference/android/support/v4/view/ViewPager.html
If that doesn't fit your design you need to override the ScrollView -> function onScrollChanged and there check if your image in screen check the image (left?) position compared to the scroll position.
You need a ListView but there isn't native HorizontallListView in android. You can try HorizontallListView by DevSmart. It's works like a native list view, with adapters and custom views if you want.
Related
I'm trying to get the postions of an ImageView after i added it to a relative Layout.
I'm adding ImageViews randomly within the relative layout by clicking on a button. So far this works very good. But why does getLeft, getRight, getTop, getBottom return 0? The Thing is, that the values are all available on the next buttonclick. So when ImageView 1 has everything 0 on the creation, it has the information if i click the button again.
So to make it clear:
Buttonclick-1 -> ImageView-1 added (outputs 0 on getLeft,Top,Right,Bottom)
Buttonclick-2 -> ImageView-2 added (ImageView-1 outputs coordinates, but ImageView-2 outputs 0)
I'm sure this has to do with the drawing of the ImageView. Surely it is not ready drawn on the view when i iterate over the children and ask for the actual positions.
Here is my code so far:
final ConstraintLayout cl = (ConstraintLayout) findViewById(R.id.constraintLayout);
final ScrollView sc=(ScrollView) findViewById(R.id.scrollView);
final Button btnButton = (Button) findViewById(R.id.buttontest);
final LinearLayout ll = (LinearLayout) findViewById(R.id.linearLayout);
final RelativeLayout rl = (RelativeLayout) findViewById(R.id.rLayout);
btnButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ImageView im= new ImageView(UserAreaActivity.this);
im.setImageResource(R.mipmap.ic_launcher);
im.setScaleType(ImageView.ScaleType.FIT_XY);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(150, 150);
Random r = new Random();
Random r2 = new Random();
int x = r.nextInt(rl.getWidth());
int y = r2.nextInt(rl.getHeight());
if(x+(layoutParams.width) >= rl.getWidth()){
x=x-layoutParams.width;
}
if(y+(layoutParams.height) >= rl.getHeight()){
y=y-layoutParams.height;
}
layoutParams.leftMargin=x;
layoutParams.topMargin=y;
im.setLayoutParams(layoutParams);
TextView tv = new TextView(UserAreaActivity.this);
tv.setText("my text");
rl.addView(im);
ll.addView(tv);
System.out.println("ID "+im.getId());
System.out.println("Left "+im.getLeft());
System.out.println("Right "+im.getRight());
System.out.println("Top "+im.getTop());
System.out.println("Bottom "+im.getBottom());
for (int i = 0; i < rl.getChildCount(); i++) {
View subView = rl.getChildAt(i);
if (subView instanceof ImageView) {
ImageView imageView = (ImageView) subView;
System.out.println("ID "+i);
System.out.println("Left "+imageView.getLeft());
System.out.println("Right "+imageView.getRight());
System.out.println("Top "+imageView.getTop());
System.out.println("Bottom "+imageView.getBottom());
}
}
}
});
When you call rl.addView(im); you are kicking off a process that will eventually involve a "layout pass" for your RelativeLayout and your ImageView. It is only after this layout pass completes that you will be able to get valid coordinates for your ImageView.
Unfortunately, there's no one-liner for "give me the coordinates after the layout pass". All the best options are asynchronous (i.e. you register some code to run once the ImageView has been laid out). I recommend ViewTreeObserver.OnGlobalLayoutListener.
Something like this:
im.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
System.out.println("ID "+im.getId());
System.out.println("Left "+im.getLeft());
System.out.println("Right "+im.getRight());
System.out.println("Top "+im.getTop());
System.out.println("Bottom "+im.getBottom());
im.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
I am currently adding ImageViews and setting the images within a ViewHolder. The creation time is not smooth and the first scroll through is not smooth either. Any suggestion or an alternative to having smooth dynamic views within a viewholder?
ViewHolder
class ViewHolderRecipientExpanded extends RecyclerView.ViewHolder {
private LinearLayout mFlagLayout;
//
public ViewHolderRecipientExpanded(View v) {
super(v);
mFlagLayout = (LinearLayout) v.findViewById(R.id.flagLayout);
//
}
public void initiateRecipientsDetails() {
Recipient recipient = objectList.get(getAdapterPosition());
int totalAmountOfCountries = recipient.getFlags() != null ? recipient.getFlags().size() : 0;
//
int countriesLimitedToThree = recipient.getFlags() != null ? (totalAmountOfCountries > 3 ? 3 : totalAmountOfCountries) : 0;
View imageView;
ImageView image;
Drawable drawable;
for (int i = 0; i < countriesLimitedToThree; i++) {
imageView = inflater.inflate(R.layout.view_image_flag, null);
image = (ImageView) imageView.findViewById(R.id.image);
drawable = new BitmapDrawable(context.getResources(), recipient.getFlags().get(i).getFlag());
image.setImageDrawable(drawable);
mFlagLayout.addView(imageView);
}
}
}
Load your images asynchronously, and cache them in memory when they are not in view. This will not block the UI thread when loading images, allowing the user to scroll even if the images aren't loaded yet. Though, it will still take some time for the images to actually appear.
There are also a number of great libraries that will handle this for you:
Picasso
Glide
Fresco
I wanted to know if my textview is within the visible screen region or not..
I have looked at links as:
Android - Get the visible area of a view?
Android: how to check if a View inside of ScrollView is visible?
But nothing seems to work for me.
Code that i used to check is
Rect rect = new Rect();
//textview intialized in onCreate as text1
text1.getHitRect(rect);
text1.measure(0, 0);
//layout is the parent layout (linear) in which i am adding the text view
Rect acctualView = new Rect(Math.round(layout.getX()), Math.round(layout.getY()),
Math.round(layout.getX() + layout.getWidth()), Math.round(layout.getY() +layout.getHeight()));
if(Rect.intersects(acctualView, rect))
{
Toast.makeText(context, "visible", Toast.LENGTH_LONG).show();
}else {
Toast.makeText(context, "Not visible ", Toast.LENGTH_LONG).show();
}
Every width and height of textview and layout is giving me 0 "zero",
Please let me know how to fix this and know whether a view is inside the visible region or not
Here is my complete activity code
public class MainActivity extends Activity {
private TextView text1, text2, text3, text4, text5, text6;
private Context context;
private LinearLayout layout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = getLayoutInflater().getContext();
layout = (LinearLayout) findViewById(R.id.parentLayout);
setTextViews();
checkViewAndUpdate();
}
private void checkViewAndUpdate() {
Rect rect = new Rect();
layout.getHitRect(rect);
if (text1.getLocalVisibleRect(rect)) {
Toast.makeText(context, "visible", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(context, "Not visible", Toast.LENGTH_LONG).show();
}
}
private void setTextViews() {
text1 = new TextView(context);
text2 = new TextView(context);
text3 = new TextView(context);
text4 = new TextView(context);
text5 = new TextView(context);
text6 = new TextView(context);
text1.setText("text1");
text2.setText("text2");
text3.setText("text3");
text4.setText("text4");
text5.setText("text5");
text6.setText("text6");
text1.setTextSize(30);
text2.setTextSize(30);
text3.setTextSize(30);
text4.setTextSize(30);
text5.setTextSize(30);
text6.setTextSize(30);
//layout.removeAllViews();
layout.addView(text1);
layout.addView(text2);
layout.addView(text3);
layout.addView(text4);
layout.addView(text5);
layout.addView(text6);
}
}
and my layout only contains LinearLayout (empty)
as i wrote on comment, checking visible rect at onCreate won't work.
Try this code at end of onCreate (instead of call checkAndUpdate() directly).
layout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
checkViewAndUpdate();
}
});
View layout does not finish though if you added view at onCreate. yes, this time user cannot see the actual activity and view's actual size are not set.
onGlobalLayout will call when view layout is ended (and activity is visible).
at that timing, we can now get size and position of them!
FYI, you can remove this listener by calling layout.getViewTreeObserver().removeOnGlobalLayoutListener(this) in onGlobalLayout()
try with this:
if (yourView.getParent().getVisibility() == View.VISIBLE) {
// visible
} else{
// hiden
}
Or this:
Rect rect = new Rect();
parentView.getHitRect(rect);
if (imageView.getLocalVisibleRect(rect)) {
// imageView is within the visible window
} else {
// imageView is not within the visible window
}
Why don't you use the for method?
for(int i = 0; i < 6; i++){
TextView tv = new TextView(context);
tv.setTextSize(30);
layout.addView(tv);
//Maybe set some listeners...
}
So in my android app I have a HorizontalScrollView that will display images. All images are downloaded an added to the View before it is avalible to the user. However when it does apear I want each image to animate in seperatly. Ive tried a LayoutTransition object attached to my layout with this code to show the views:
transition = new LayoutTransition();
transition.setStagger(LayoutTransition.APPEARING, 500);
transition.setDuration(1000);
transition.setAnimateParentHierarchy(true);
for (int i = 0; i < mGallery.getChildCount(); i++) {
mGallery.getChildAt(i).setVisibility(View.VISIBLE);
transition.showChild(mGallery, mGallery.getChildAt(i));
}
I have also tried this method using the the AnimationEndListener, and a recursive animateView() method
private void animateView(final int index) {
if (mGallery.getChildAt(index) == null)
return;
final View child = mGallery.getChildAt(index);
final Animation an = AnimationUtils.loadAnimation(mRootView.getContext(), R.anim.slideup);
AnimationEndListener listener = new AnimationEndListener() {
#Override
public void onAnimationEnd(Animation animation) {
animateView(index + 1);
}
};
an.setAnimationListener(listener);
child.setAnimation(an);
child.setVisibility(View.VISIBLE);
an.start();
}
The first method is preferable however it always animates in all my images at the same time. The second method kind of works, however it will apply the animation to the first view and the all subsequent views will appear in sequence without the animation playing.
Is it possible to access a child view of a gridview that is not on screen and change it?
I have tried using getCount() when iterating over the gridview, but when it reaches the children that are not visible(on screen display) they lose their type (they should be ImageViews) and I can not do anything with them; I want to change their image.
An snippet of code;
GridView gridView = (GridView) findViewById(R.id.grid_view);
Integer items = gridView.getCount();
Log.d(TAG, "1)child count::"+((ViewGroup)gridView).getChildCount());
Log.d(TAG, "2)get count::"+items);
for (int i=0; i < items; ++i) {
View nextChild = ((ViewGroup)gridView).getChildAt(i);
if (nextChild instanceof ImageView) {
Log.i(TAG, i+" is imageview");
((ImageView) nextChild).setImageResource(R.drawable.arrow2);
} else {
Log.i(TAG, i+" is NOT imageview");
}
}
You should be setting the image resource in the adapter's getView() method. Then the code will run as and when they become visible, rather than trying to force it before they come into the screen.