View Layout_Margin doesn't work inside the FrameLayout - android

I have a frameLayout and inside this layout there are several Views, and I want to margin each view from the top to a specific distance .
I have tried the following code, but it seems that it doesn't work
FrameLayout lytBoard = (FrameLayout) findViewById(R.id.lytBoard);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(width, height);
params.setMargins((int)board.cells.get(i).x1, (int)board.cells.get(i).y1, 0, 0);
CellView cv = new CellView(getApplicationContext());
cv.setLayoutParams(params);
lytBoard.addView(cv);
Cell View class:
public class CellView extends View {
public CellView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int size = Math.min(getMeasuredWidth(), getMeasuredHeight());
setMeasuredDimension(size, size);
}
}

You are setting to the CellView, subclass of View, a layoutParams of the type LinearLayout.LayoutParams. In the definition of the method in View, the method setLayoutParams receives params of the type ViewGroup.LayoutParams and ViewGroup does not contains a margin property, so this may be the cause of the problem.
You can try to subclass LinearLayout instead of View.

Related

Android 1 Parent ScrollView, 3 TextView with vertical scrolling

I have a question with the android programming.
I have a parent scrollview for whole activity, and there are three textviews with scrolling function. However, when I used the following code it seems to be not working at all. Only the parent scrolling is available.
final View view = inflater.inflate(R.layout.activity_register_terms_fragment, container, false);
TextView basicTermView = (TextView) view.findViewById(R.id.register_terms_basic_info);
TextView purposeTermView = (TextView) view.findViewById(R.id.register_terms_purpose_info);
TextView provideTermView = (TextView) view.findViewById(R.id.register_terms_provide_info);
TextView previous = (TextView) view.findViewById(R.id.register_terms_pre);
TextView next = (TextView) view.findViewById(R.id.register_terms_next);
basicTermView.setMovementMethod(new ScrollingMovementMethod());
purposeTermView.setMovementMethod(new ScrollingMovementMethod());
provideTermView.setMovementMethod(new ScrollingMovementMethod());
How should I change the codes?
Thank you for helping me!
You can't have a scrollable View, like TextView or ListView or RecyclerView, inside a ScrollView. So use your simple TextView inside a normal layout and add android:scrollbars property to it or you can use view's customised class which will calculate view's width/height programatically and use ScrollView as it's parent.
As an example, to use Listview inside scrollview, we need to use following customised class of Listview which will calculate list items height and set it.
public class ExpandedListView extends ListView {
private ViewGroup.LayoutParams params;
private int old_count = 0;
public ExpandedListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ExpandedListView(Context context) {
super(context);
}
public ExpandedListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightMeasureSpec_custom = MeasureSpec.makeMeasureSpec(
Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec_custom);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
}
#Override
protected void onDraw(Canvas canvas) {
if (getCount() != old_count) {
this.setScrollContainer(false);
old_count = getCount();
params = getLayoutParams();
params.height = getCount()
* (old_count > 0 ? getChildAt(0).getHeight() : 0);
setLayoutParams(params);
}
super.onDraw(canvas);
}
}

Gravity of a child TextView does not work with custom ViewGroup

I have the following classes:
public class MyGridView extends ViewGroup {
...
#Override
public void onSizeChanged(int xNew, int yNew, int xOld, int yOld) {
super.onSizeChanged(xNew, yNew, xOld, yOld);
// do calculations for drawing bounds of child views
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
View child;
Rect rect;
for (int i=0; i < getChildCount(); i++) {
child = getChildAt(i);
rect = getBoundsAtChildIndex(i)
child.layout(rect.left, rect.top, rect.right, rect.bottom);
}
}
...
}
AND
public class MyAdapter {
public void setMyGridView(MyGridView myGridView) {
// add a single TextView to my grid view
textView = createTextView(context);
addTextViewToGrid(textView);
container.add(textView);
}
private TextView createTextView(Context context) {
TextView textView = new TextView(context);
textView.setText("1");
textView.setTextColor(0xffffffff);
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12f);
textView.setTypeface(typeface);
textView.setGravity(Gravity.CENTER | Gravity.FILL);
// textView.setGravity(Gravity.RIGHT); // other tries to see gravity
// textView.setGravity(Gravity.FILL);
return textView;
}
private void addTextViewToGrid(TextView textView) {
myGridViewPtr.addView(textView, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
}
...
}
I have created a custom grid view and adapter, where its children draw to the screen to the correct size and location. However, their gravity, set in the createTextView() is not observed.
I can set a background image to the textView and see it fill its space in the grid.
In contrast, the text in textView always draws in its top left corner, and it always stays at the text size I set it at 12sp, rather than scale to fit using Gravity.FILL.
Preferably, the text would scale to fit and center within the textview.
EDIT
I have added the following method for onMeasure() in ViewGroup:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
doUpdateOnSizeChanged(width, height);
for (int i = 0; i < getChildCount(); i++)
getChildAt(i).measure((int) viewDim.x, (int) viewDim.y);
setMeasuredDimension(width, height);
}
The children are supposed to be the same height, width as each other. The android example code for ViewGroup is more sophisticated than I require. For instance, I am not getting the max of all children width, height.
For my onMeasure(), the child width, height in ViewDim is width, height integers that are computed in doUpdateOnSizeChanged to be less than getMeasuredWidth, getMeasuredHeight.
The result is now text aligns in the bottom-left corner of the TextView.
onMeasure needed minor corrections, basically needing to rely on MeasureSpec:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width, height;
int cWidth1, cHeight1;
width = MeasureSpec.getSize(widthMeasureSpec);
height = MeasureSpec.getSize(heightMeasureSpec);
doUpdateOnSizeChanged(width, height);
cWidth1 = (int)viewDim.x; cHeight1 = (int)viewDim.y;
measureChildren(MeasureSpec.makeMeasureSpec(cWidth1, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(cHeight1, MeasureSpec.EXACTLY));
setMeasuredDimension(width, height);
}

Proper way to implement custom View and dynamically added children

I'm extending a HorizontalScrollView. This layout will add children to a LinearLayout when the public setItems method is called. These children views are dynamically inflated and their widths depend on the parent (fill parent when only one view, and 1/2 parent when >= 2 items).
<!-- custom_layout.xml -->
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/container"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="match_parent">
</LinearLayout>
</merge>
Sometimes, setItems is called in a callback of a network request, so the layout might already be fully inflated. Where should I be calling updateView which needs the parent's width and to add inflated children? I put the call in both onSizeChanged and setItems like below.
public class CustomLayout extends HorizontalScrollView {
private LinearLayout container;
private List<Item> items;
public void setItems(List<Item> items) {
this.items = items;
updateView();
}
public CustomLayout(Context context) {
super(context);
init();
}
public CustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
View v = inflate(getContext(), R.layout.custom_layout, this);
container = (LinearLayout) v.findViewById(R.id.container);
updateView();
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
updateView();
}
public void updateView() {
if (items == null) {
return;
}
container.removeAllViews();
int width = getWidth();
if (items.size() > 1) {
width = width * 1 / 2;
}
for (final Item item : items) {
View child = LayoutInflator.from(getContext(), R.layout.custom_item, container, false);
child.setLayoutParams(new LinearLayout.LayoutParams(width, LinearLayout.LayoutParams.MATCH_PARENT));
container.addView(child);
}
}
}
I don't know why this way does not always work. Sometimes, the child, container just fails to render for some reason. Seeing the layout hierarchy shows that HorizontalScrollView has no children. What happened to container?
Also, it seems that even when placed in a network call, setItems is called before onSizeChanged, which makes me think that onSizeChanged is the wrong place to do updateView?
Try to place the updateView() method inside onMeasure instead of onSizeChanged, and use getMeasuredWidth() instead of getWidth():
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
updateView();
}
public void updateView() {
if (items == null) {
return;
}
container.removeAllViews();
int width = getMeasuredWidth();
if (items.size() > 1) {
width = width * 1 / 2;
}
for (final Item item : items) {
View child = LayoutInflator.from(getContext(), R.layout.custom_item, container, false);
child.setLayoutParams(new LinearLayout.LayoutParams(width, LinearLayout.LayoutParams.MATCH_PARENT));
container.addView(child);
}
}
Also, if you call setItems() method from callback of async request, dont forget to run it on UI thread by using runOnUiThread method of Activity.

Android: Set match_parent in custom view programmatically

I implemented a custom view where I can paint. I want to set it to MATCH_PARENT, so that it fills the screen independent of the screen orientation. When I change orientation to landscape it fills just 50% of width.
I changed onMeasure() but there was no effect:
public class DrawScreen extends View{
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if(context.getResources().getConfiguration().orientation==Configuration.ORIENTATION_LANDSCAPE){
setMeasuredDimension(screenWidth, screenHeight);
}else{
setMeasuredDimension(screenHeight, screenWidth);
}
}
}
public class MyService extends Service{
...
windowManager.addView(toolbox, params);
}
You could try the following:
final DrawScreen view = new DrawScreen(...);
final LayoutParams lp = new LayoutParams(MATCH_PARENT, MATCH_PARENT);
view.setLayoutParams(lp);

How to inflate xml-layout into child View

I'm trying to inflate xml-layout into custom View-class, which is then placed into LinearLayout in actual software. After some Googling I managed to create custom class which inflates the layout by using following class:
public class LitteringView extends RelativeLayout
{
public LitteringView(Context context) {
super(context);
// TODO Auto-generated constructor stub
View.inflate(context, R.layout.littering_layout, this);
}
public LitteringView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
for(int i = 0 ; i < getChildCount() ; i++){
getChildAt(i).layout(l, t, r, b);
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
Now it definitely inflates when I add it to the linearlayout:
layout.addView(new LitteringView(getActivity()));
Problem is that it takes the whole screen while the screen should be divided equally between LinearLayout's three children. Other Views are also created dynamically. How I can prevent it from taking the whole space?
I'm trying to create one custom View/class and package its functions inside it which I then could easily add to the parent layout (in this case the LinearLayout) while wanting to utilize xml to define the layout of the View.
You need to use 'weight' for your views. A simple example for this:
Layout.LayoutParams params = new LinearLayout.LayoutParams(width, height, weight);
You need to optimize it for your case. Give all your views a weight value equally(eg: 1 for all of them) and they should share the view.

Categories

Resources