Android: Adding Layouts to HorizontalScrollView - java

I'm trying to create something similar to the home screen layout. This consists of multiple vertically oriented LinearLayouts within a single horizontally oriented LinearLayout, all housed in a HorizontalScrollView. I've written it as a custom class called 'HomeLayout' extending HorizontalScrollView.
LinearLayout wrapper = new LinearLayout(getContext());
LayoutParams params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
wrapper.setLayoutParams(params);
wrapper.setOrientation(LinearLayout.HORIZONTAL);
addView(wrapper);
for(int i = 0; i < 2; i++) {
LinearLayout linear = (LinearLayout) View.inflate(getContext(), R.layout.screens, null);
linear.setLayoutParams(params);
wrapper.addView(linear);
}
The problem is 'screen2' has width '0' when added.
This only works if I call this in onDraw() and use getMeasuredWidth() and getMeasuredHeight() instead of LayoutParams.FILL_PARENT. And even then, I'm not sure if that's the right thing to do.
Furthermore I'm unable to reference the views 'wrapper', 'screen1', 'screen2' in onCreate().
I loosely followed this link: http://blog.velir.com/index.php/2010/11/17/android-snapping-horizontal-scroll/
What am I doing wrong?

Hoping this helps others. I solved my problem by implementing onMeasure() in the following way:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
for(int i = 0; i < mWrapper.getChildCount(); i++) {
View child = mWrapper.getChildAt(i);
child.setLayoutParams(new LinearLayout.LayoutParams(width,
LayoutParams.FILL_PARENT));
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

Related

Android Java Vertical scroll view size

I have a very simple activity with a vertical scroll view (mainscreen.xml below).
This vertical scroll view has a linear layout child view.
I dynamically add text views as children to this linear layout (populateLinlayWithTextViews() below).
Later, after these text view children have been added, I can scroll the vertical scroll view (onScrollChange() below).
And I can measure the visible part of the vertical scroll view (getHeight()).
My problem is that I need to know earlier than at scroll time what is the maximum height of the visible part of the vertical scroll view.
mainscreen.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:id="#+id/scrollview"
android:layout_height="wrap_content"
android:layout_width="match_parent">
<LinearLayout
android:id="#+id/linlay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
</ScrollView>
</LinearLayout>
MyActivity.java
protected void onCreate(#Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.mainscreen);
Resources resources = getResources();
int n_statusBarHeight = resources.getIdentifier( "status_bar_height", "dimen", "android"); // 36px
populateLinlayWithTextViews();
// HERE
ScrollView sv = sv = findViewById(R.id.scrollview);
int n_svVisiblePartHeight = sv.getHeight(); // 0px
View.OnScrollChangeListener onScrollChangedListener
= new View.OnScrollChangeListener()
{
#Override
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY)
{
ScrollView sv = sv = findViewById(R.id.scrollview);
int n_svVisiblePartHeight = sv.getHeight(); // 680px
}
}
sv = findViewById(R.id.scrollview);
sv.setOnScrollChangeListener(onScrollChangedListener);
}
public void populateLinlayWithTextViews()
{
LinearLayout linlay = findViewById(R.id.linlay);
int n_viewHeight = 81; // px
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
n_viewHeight);
for(int n_i=0 ; n_i<20 ; n_i++)
{
TextView tvNew = new TextView(m_app);
tvNew.setText(n_i + " - test");
tvNew.setTextColor(Color.rgb(0, 0, 0)); // black
tvNew.setTextSize(TypedValue.COMPLEX_UNIT_SP, 40);
tvNew.setLayoutParams(params);
linlay.addView(tvNew, linlay.getChildCount());
}
}
When I scroll the vertical scroll view, I can see that n_svVisiblePartHeight's value is 680px.
But I need to know this value earlier // HERE.
But // HERE, the value I get is 0px.
Can you help me?
EDIT 1
I need to know this height because:
example 1: I would like to know initially the index of the text view that will be at the bottom of the (visible) screen after I have populated
the linear layout with text views ;
example 2: given this height X I'm looking for, let's say that I would like 10 text views to be visible initially, I would like to determine X/10 which will be the height of the text views, so that I can put it dynamically in n_viewHeight.
EDIT 2
I just need to know the height of the red arrow below, a soon as I can in the lifecycle of the activity and preferably before I add text views to the linear layout or right after (// HERE):
EDIT 3
I don't know if theoretically that's true:
n_scrollViewVisibleHeightInPx =
n_displayHeightInPx
- n_actionBarHeight
- n_navigationBarHeightInPx
+ n_statusBarHeightInPx;
... but practically n_scrollViewVisibleHeightInPx is the height of the space designated by the red arrow in the image above.
To get these various heights, below are the methods I used:
private int returnStatusBarHeightInPx()
{
int n_statusBarHeightInPx = -1;
Resources resources = null;
int n_idStatusBarHeight = -1;
resources = getResources();
n_idStatusBarHeight = resources.getIdentifier( "status_bar_height", "dimen", "android");
if(n_idStatusBarHeight > 0)
{
n_statusBarHeightInPx = getResources().getDimensionPixelSize(n_idStatusBarHeight);
}
else
{
n_statusBarHeightInPx = 0;
}
return n_statusBarHeightInPx;
}
private int returnNavigationBarHeightInPx()
{
int n_navigationBarHeightInPx = -1;
Resources resources = null;
int n_idNavigationBarHeight = -1;
resources = getResources();
n_idNavigationBarHeight = resources.getIdentifier("navigation_bar_height", "dimen", "android");
if(n_idNavigationBarHeight > 0)
{
n_navigationBarHeightInPx = getResources().getDimensionPixelSize(n_idNavigationBarHeight);
}
else
{
n_navigationBarHeightInPx = 0;
}
return n_navigationBarHeightInPx;
}
private int returnActionBarHeightInPx()
{
TypedValue typedValue = null;
int n_actionBarHeightInPx = -1;
typedValue = new TypedValue();
if(getTheme().resolveAttribute(android.R.attr.actionBarSize, typedValue, true))
{
n_actionBarHeightInPx =
TypedValue.complexToDimensionPixelSize(
typedValue.data,
getResources().getDisplayMetrics());
}
return n_actionBarHeightInPx;
}
private int returnDisplayHeightInPx()
{
DisplayMetrics displayMetrics = null;
int n_displayHeightInPx = -1;
displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
n_displayHeightInPx = displayMetrics.heightPixels;
return n_displayHeightInPx;
}
Why do you want to know it in "//here". You do not use that variable there. Your code does not explain, why it is not possible, to define the variable on scroll time.

HorizontalScrollView scrollTo doesn't scroll above original width of the view

I have a custom view containing an HorizontalScrollView. The width of the scroll view is match_parent, and the width of its children is initially programmatically set based on the value of an attribute of the custom view. At some point, the width of the children of the scroll view are updated (increased) programmatically. The problem is that, after the update, the scrollTo method is still unable to scroll above the original width value (the same for scrollBy).
The enclosing view (the custom one) have a left and right padding equal to half the screen, if this is relevant.
Example:
Initial HorizontalScrollView's children width: 1000;
HorizontalScrollView's parent width: 1080;
HorizontalScrollView's left/right padding = 540;
New HorizontalScrollView's children width: 2000;
scrollTo(1100, 0) = scrollTo(1000, 0); <---- here is the problem
Relevant code:
Custom view initialization:
private void init(AttributeSet attrs, int defStyle) {
inflate(getContext(), R.layout.timeline_view, this);
// Load attributes
final TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.TimelineView, defStyle, 0);
m_timelineInitialLength = a.getDimension(R.styleable.TimelineView_timelineInitialLength, DEFAULT_LENGTH);
a.recycle();
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
LinearLayout timelineWidget = findViewById(R.id.timeline_widget);
int horizontalPadding = displayMetrics.widthPixels / 2;
timelineWidget.setPadding(horizontalPadding, 0, horizontalPadding, 0);
m_horizontalScrollView = findViewById(R.id.scroll_view);
m_horizontalScrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
m_currentScrollX = m_horizontalScrollView.getScrollX();
}
});
m_currentScrollX = m_horizontalScrollView.getScrollX();
update(m_timelineInitialLength, m_timelineInitialStepWidth, m_currentScrollX);
}
Update and scrollTo (this method is called also at the end of the initialization. m_rulerView and m_timelineLayout are the children of the HorizontalScrollView):
private void update(float timelineLength, float timelineStepWidth, int currentScrollX) {
m_rulerView = findViewById(R.id.ruler);
m_rulerView.setIndicator(null);
m_rulerView.getLayoutParams().width = (int) timelineLength;
m_rulerView.requestLayout();
m_timelineLayout = findViewById(R.id.timeline);
m_timelineLayout.getLayoutParams().width = (int) timelineLength;
m_timelineLayout.requestLayout();
m_horizontalScrollView.scrollTo(currentScrollX, 0);
}
The problem seems to be that the scrolling happens before the children have been redrawn, before calling requestLayout only schedule the update of the view to be executed at some point in the future (the same for invalidate()).
I solved using a workaround, by executing scrollTo after some time:
new Handler().postDelayed(() -> ((Activity) getContext()).runOnUiThread(() -> m_horizontalScrollView.scrollTo(currentScrollX, 0)), 200);
I'm still open to better solutions.

How to calculate the height of a scrollView using ViewTreeObserver?

I have a ScrollView with some layouts in it, called the fragment_about_sl.xml.And the class associated to it is called AboutSLFragment.java.I want to calculate the height of the scrollView to get height ratio for an image.I researched the web and found this code.
ViewTreeObserver vto = scrollView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
scrollView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
int scrollViewHeightInPixels = layout.getMeasuredHeight();
//This is equal with %45 weight you tried before
int height = (scrollViewHeightInPixels * 45) / 100;
ViewGroup.LayoutParams pagerParams = viewPager.getLayoutParams();
pagerParams.height = height;
}
});
As shown in the above code.It just have said scrollView instead of getting it from findViewById().And also in the below code,it just says layout.getMeasuredHeight() instead of specifying the name of layout.
int scrollViewHeightInPixels = layout.getMeasuredHeight();
The question is that I want to know what is meant by layout here(above).What should I put there,is it the id of the scrollView?If so I developed a code myself and I want to know whether it is correct.Please help me I am new to android.The id of the scrollview I used is aboutslscrollview.
final View view = rootView.findViewById(R.id.aboutslscrollview);
ViewTreeObserver vto = view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
int scrollViewHeightInPixels = view.getMeasuredHeight();
//This is equal with %45 weight you tried before
int height = (scrollViewHeightInPixels * 45) / 100;
ViewGroup.LayoutParams pagerParams = viewPager.getLayoutParams();
pagerParams.height = height;
}
});

My ImageButton isn't rescaling like the code is telling it to. Infact it's not doing anything

My ImageButton isn't rescaling like the code is telling it to. Infact it's not doing anything.
Code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayShowCustomEnabled(true);
setTitle("Valour");
setContentView(R.layout.activity_main_screen);
getScreenRes();
}
public void getScreenRes() {
DisplayMetrics display = this.getResources().getDisplayMetrics();
int width = display.widthPixels;
int height = display.heightPixels;
int buttonheight = display.heightPixels / 8;
double buttonwidth = buttonheight * 2.66666667;
int buttonwidthint = (int) Math.round(buttonwidth);
ImageButton firsttimeFB = (ImageButton) findViewById(R.id.firsttime_fb);
firsttimeFB.getLayoutParams().width = buttonwidthint;
firsttimeFB.getLayoutParams().height = buttonheight;
}
XML:
<ImageButton
android:layout_width="50dip"
android:layout_height="50dip"
android:background="#drawable/facebook"
android:id="#+id/firsttime_fb"
/>
And it ends up looking like this:
call setLayoutParams() from the firsttimeFB after change the width and height.
Try using android:src and change the ScaleType, see if that makes it scale better.
Also after setting the width and hight by calling getLayoutParams() you should call request layout or the changes will not be displayed.
If you want to set the width based on the height or vice versa you should create your own custom ImageButton class which contains the following code:
public class CustomImageButton extends ImageButton{
...
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height = getMeasuredHeight();
float constant = 2.6666;
setMeasuredDimension(height * constant, height);
}
...
}
What I want to add is that I am wondering what you are trying to do. I can not think of a senario in which I need the screensize to determen the size of the button. Try to rethink your approach and ask yourself if what you are doing is making any sense.
Try this:
public void getScreenRes(){
DisplayMetrics display = this.getResources().getDisplayMetrics();
int width = display.widthPixels;
int height = display.heightPixels;
int buttonheight = display.heightPixels / 8;
double buttonwidth = buttonheight * 2.66666667;
int buttonwidthint = (int) Math.round(buttonwidth);
ImageButton firsttimeFB = (ImageButton) findViewById(R.id.firsttime_fb);
LayoutParams lp = firsttimeFB.getLayoutParams();
lp.width = buttonwidthint;
lp.height = buttonheight;
firsttimeFB.setLayoutParams(lp);
}

Android Issue with activity context

So I have a helper class that reuses a lot of code through out the application, one of the methods is shown below:
public void setTitleTextSize(final int id){
infoButton = ((Activity) context).findViewById(R.id.info_button);
ViewTreeObserver customTitleScale = infoButton.getViewTreeObserver();
customTitleScale.addOnPreDrawListener(new OnPreDrawListener() {
#Override
public boolean onPreDraw() {
int infoWidth = infoButton.getMeasuredWidth();
if(infoWidth !=0){
if(Build.VERSION.SDK_INT >= 11.0){
TypedValue tv = new TypedValue();
((Activity) context).getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true);
actionBarWidth = ((Activity) context).getResources().getDimensionPixelSize(tv.resourceId);
}
DisplayMetrics metrics = new DisplayMetrics();
Display Screen = ((Activity) context).getWindowManager().getDefaultDisplay();
Screen.getMetrics(metrics);
int screenWidth = metrics.widthPixels;
int titleWidth = screenWidth - infoWidth - actionBarWidth;
TextView titleText = (TextView) ((Activity) context).findViewById(R.id.title_text);
titleText.setText(id);
TextPaint paint = titleText.getPaint();
Rect rect = new Rect();
String text = String.valueOf(titleText.getText());
int textLength = text.length();
paint.getTextBounds(text, 0, textLength, rect);
if(rect.width() > titleWidth){
float scale = (float) titleWidth / (float) rect.width();
float textSize = titleText.getTextSize();
float scaleSize = (float) (textSize * (scale*0.8));
titleText.setTextSize(TypedValue.COMPLEX_UNIT_PX, scaleSize);
}
infoButton.getViewTreeObserver().removeOnPreDrawListener(this);
}
return false;
}
});
}
I use this particular method on all of my activities.
The problem I've got is I don't want to display the infoButton on every activity but when I add View infoButton = findViewById(R.id.info_button); infoButton.setVisibility(View.GONE); to the activity, the screen is just black.
So I was thinking on how to do this and the only thing I can thing of is to pass a boolean into the method stating whether the info view is visible or not. I suppose I'd do an if statement saying `if true then display it, if false then don't but I can't figure out how to do this.
Any help would be amazing thanks.
Try to use method infoButton.getVisibility()
it will return you the integer to get visibility status of you widget.
I figured out the issue I was having, My logic wasn't correct for the if statement if(infoWidth !=0). The code wasn't running if the activity didn't have an info button because the infoWidth would always be zero. So I changed the logic to if(infoWidth !=0 || !displayInfoButton) which works perfectly.

Categories