I am trying to obtain width of HorizontalScrollView child however my width is always the same width as my HorizontalScrollView
I tried overriding onMeasure and onLayout and calling measure() but I get either 0, 100, or the same width as my HorizontalScrollView
my current code is
void adjustClip(int r, int b) {
// ommited
}
void getWindowSize() {
if (mOrientation == VERTICAL) {
windowHeight = document.getHeight();
} else {
windowWidth = document.getWidth();
}
}
boolean getDocumentSizeTypeRecyclerView() {
// ommited
}
void printWidths(View view) {
Log.d(TAG, "view.getRight() = [" + (view.getRight()) + "]");
Log.d(TAG, "view.getWidth() = [" + (view.getWidth()) + "]");
Log.d(TAG, "view.getMeasuredWidth() = [" + (view.getMeasuredWidth()) + "]");
Log.d(TAG, "view.getMeasuredWidthAndState() = [" + (view.getMeasuredWidthAndState()) + "]");
Log.d(TAG, "view.getMinimumWidth() = [" + (view.getMinimumWidth()) + "]");
Log.d(TAG, "view.getVerticalScrollbarWidth() = [" + (view.getVerticalScrollbarWidth()) + "]");
}
boolean getDocumentSizeTypeHorizontalScrollView() {
if (document instanceof HorizontalScrollView) {
HorizontalScrollView horizontalScrollView = (HorizontalScrollView) document;
printWidths(horizontalScrollView);
printWidths(horizontalScrollView.getChildAt(0));
View child = ((ViewGroup) document).getChildAt(0);
documentWidth = child.getWidth();
return true;
} else return false;
}
boolean getDocumentSize() {
if (getDocumentSizeTypeRecyclerView()) return true;
if (getDocumentSizeTypeHorizontalScrollView()) return true;
return false;
}
void setThumbSize(int b, int r) {
if (mOrientation == VERTICAL) {
documentHeightDivWindowHeight = documentHeight / windowHeight;
float thumbHeight = b / documentHeightDivWindowHeight;
clip.setHeight((int) thumbHeight);
} else {
documentWidthDivWindowWidth = documentWidth / windowWidth;
float thumbWidth = r / documentWidthDivWindowWidth;
clip.setWidth((int) thumbWidth);
}
}
void doScroll(int r, int b) {
if (document != null) {
getWindowSize();
if (getDocumentSize()) {
setThumbSize(b, r);
if (!scrolling) scrollDocument();
}
}
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (DEBUG) Log.d(TAG, "onLayout() called with: changed = [" + changed + "], l = [" + l + "], t = [" + t + "], r = [" + r + "], b = [" + b + "]");
if (layout) {
layout = false;
} else {
layout = true;
adjustClip(r, b);
doScroll(r, b);
}
}
and this is what it prints
D/ScrollBarView: onLayout() called with: changed = [false], l = [80], t = [0], r = [760], b = [80]
// HorizontalScrollView
view.getRight() = [1140]
view.getWidth() = [840]
view.getMeasuredWidth() = [840]
view.getMeasuredWidthAndState() = [840]
view.getMinimumWidth() = [0]
view.getVerticalScrollbarWidth() = [14]
// child
view.getRight() = [840]
view.getWidth() = [840]
view.getMeasuredWidth() = [840]
view.getMeasuredWidthAndState() = [840]
view.getMinimumWidth() = [0]
view.getVerticalScrollbarWidth() = [0]
as I am trying to make an external scroll bar view for android, since I am unable to modify the internal scroll bar of ScrollView and related
this is what my view looks like, with the scroll view child highlighted
If I understand correctly, your measuring size of views before they have been calculated and placed on the screen.
I think you could add a listener which will run after layout have been calculated
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
printWidths(view);
}
});
Bellow how It can be used in the code:
Main Activity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ExternalScrollBar esb = (ExternalScrollBar) findViewById(R.id.esb);
esb.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
esb.getViewTreeObserver().removeOnGlobalLayoutListener(this);
LinearLayout ll = (LinearLayout) esb.getChildAt(0);
TextView tv = (TextView) ll.getChildAt(0);
TextView tv2 = (TextView) ll.getChildAt(1);
printWidths(esb);
printWidths(tv);
printWidths(tv2);
}
});
}
void printWidths(View view) {
System.out.println("view.getRight() = [" + (view.getRight()) + "]");
System.out.println("view.getWidth() = [" + (view.getWidth()) + "]");
System.out.println("view.getMeasuredWidth() = [" + (view.getMeasuredWidth()) + "]");
System.out.println("view.getMeasuredWidthAndState() = [" + (view.getMeasuredWidthAndState()) + "]");
System.out.println("view.getMinimumWidth() = [" + (view.getMinimumWidth()) + "]");
System.out.println("view.getVerticalScrollbarWidth() = [" + (view.getVerticalScrollbarWidth()) + "]");
}
}
ExternalScrollBar
public class ExternalScrollBar extends HorizontalScrollView {
public ExternalScrollBar(Context context) {
super(context);
}
public ExternalScrollBar(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
System.out.println(this.getWidth());
System.out.println(((LinearLayout)this.getChildAt(0)).getChildAt(0).getWidth());
System.out.println(((LinearLayout)this.getChildAt(0)).getChildAt(1).getWidth());
}
}
activity_layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.stackhorizontalchild.ExternalScrollBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/esb"
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World 5212! "
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello Wor!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
</com.example.stackhorizontalchild.ExternalScrollBar>
</LinearLayout>
run result:
I/System.out: 472 //onLayout
I/System.out: 302
I/System.out: 170
view.getRight() = [472] //ExternalScrollBar
view.getWidth() = [472]
view.getMeasuredWidth() = [472]
view.getMeasuredWidthAndState() = [472]
view.getMinimumWidth() = [0]
view.getVerticalScrollbarWidth() = [11]
view.getRight() = [302] //Child TextView1
view.getWidth() = [302]
view.getMeasuredWidth() = [302]
view.getMeasuredWidthAndState() = [302]
view.getMinimumWidth() = [0]
view.getVerticalScrollbarWidth() = [0]
view.getRight() = [472] //Child TextView2
view.getWidth() = [170]
view.getMeasuredWidth() = [170]
view.getMeasuredWidthAndState() = [170]
view.getMinimumWidth() = [0]
view.getVerticalScrollbarWidth() = [0]
it seems like
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
View child = horizontalScrollView.getChildAt(0);
if (child == null) {
width = child.getWidth();
}
}
}
is the correct way to get the width of a horizontal scroll view
Related
I'm trying to create an app that is accessing the camera when a button is pressed, and after the photo is taken, to display it on the screen, and then be able to find the color of the selected pixel.
Somehow, the photo displayed is very small, and the x,y are displaying colors even if i am selecting a pixel that is not from the image.
I tried to change the layout's height and width to a specific value, and the photo is pixelated, even if the resolution of the photo is quite big.
I want to be able to correctly display the pixel color from the image only, not outside of it, and the image to be well-scaled on the screen.
Here is my code, hope you guys can help me, I'm a newbie. Thank you a lot!
MainActivity.Java
public class MainActivity extends Activity {
TextView touchedXY, invertedXY, imgSize, colorRGB;
ImageView imgSource1;
Button b;
static final int CAMREQUEST = 1;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
touchedXY = (TextView) findViewById(R.id.xy);
invertedXY = (TextView) findViewById(R.id.invertedxy);
imgSize = (TextView) findViewById(R.id.size);
colorRGB = (TextView) findViewById(R.id.colorrgb);
b = (Button)findViewById(R.id.Button01);
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMREQUEST);
}
});
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CAMREQUEST) {
Bitmap image = (Bitmap) data.getExtras().get("data");
imgSource1 = (ImageView) findViewById(R.id.source1);
imgSource1.setImageBitmap(image);
imgSource1.setOnTouchListener(imgSourceOnTouchListener);
}
}
OnTouchListener imgSourceOnTouchListener
= new OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
float[] eventXY = new float[]{eventX, eventY};
Matrix invertMatrix = new Matrix();
((ImageView) view).getImageMatrix().invert(invertMatrix);
invertMatrix.mapPoints(eventXY);
int x = Integer.valueOf((int) eventXY[0]);
int y = Integer.valueOf((int) eventXY[1]);
touchedXY.setText(
"touched position: "
+ String.valueOf(eventX) + " / "
+ String.valueOf(eventY));
invertedXY.setText(
"touched position: "
+ String.valueOf(x) + " / "
+ String.valueOf(y));
Drawable imgDrawable = ((ImageView) view).getDrawable();
Bitmap bitmap = ((BitmapDrawable) imgDrawable).getBitmap();
imgSize.setText(
"drawable size: "
+ String.valueOf(bitmap.getWidth()) + " / "
+ String.valueOf(bitmap.getHeight()));
//Limit x, y range within bitmap
if (x < 0) {
x = 0;
} else if (x > (bitmap.getWidth() - 1)) {
x = bitmap.getWidth() - 1;
}
if (y < 0) {
y = 0;
} else if (y > (bitmap.getHeight() - 1)) {
y = bitmap.getHeight() - 1;
}
int touchedRGB = bitmap.getPixel(x, y);
colorRGB.setText("touched color: " + "#" + Integer.toHexString(touchedRGB));
colorRGB.setTextColor(touchedRGB);
return true;
}
};
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#00ffffff">
<TextView
android:id="#+id/xy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="touched position: "
android:layout_marginLeft="15dp"
android:layout_marginTop="5dp" />
<TextView
android:id="#+id/invertedxy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="inverted touched position: "
android:layout_marginLeft="15dp"
android:layout_marginTop="5dp" />
<TextView
android:id="#+id/size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="drawable size: "
android:layout_marginLeft="15dp"
android:layout_marginTop="5dp" />
<TextView
android:id="#+id/colorrgb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="touched color: "
android:layout_marginLeft="15dp"
android:layout_marginTop="5dp" />
<ImageView
android:id="#+id/source1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Take Picture"
android:id="#+id/Button01"
android:layout_gravity="center_horizontal" />
Use This Code may be its work fine
public class MainActivity extends Activity {
TextView touchedXY, invertedXY, imgSize, colorRGB;
ImageView imgSource1;
Button b;
static final int CAMREQUEST = 1;
Uri fileUri;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
touchedXY = (TextView) findViewById(R.id.xy);
invertedXY = (TextView) findViewById(R.id.invertedxy);
imgSize = (TextView) findViewById(R.id.size);
colorRGB = (TextView) findViewById(R.id.colorrgb);
b = (Button) findViewById(R.id.Button01);
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
fileUri = null;
fileUri = Uri.fromFile(new File(mediapath.getPath(), "Image" + System.currentTimeMillis() + ".jpg"));
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
startActivityForResult(cameraIntent, CAMREQUEST);
}
});
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CAMREQUEST) {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), fileUri);
imgSource1 = (ImageView) findViewById(R.id.source1);
imgSource1.setImageBitmap(image);
imgSource1.setOnTouchListener(imgSourceOnTouchListener);
}
}
OnTouchListener imgSourceOnTouchListener
= new OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
float[] eventXY = new float[]{eventX, eventY};
Matrix invertMatrix = new Matrix();
((ImageView) view).getImageMatrix().invert(invertMatrix);
invertMatrix.mapPoints(eventXY);
int x = Integer.valueOf((int) eventXY[0]);
int y = Integer.valueOf((int) eventXY[1]);
touchedXY.setText(
"touched position: "
+ String.valueOf(eventX) + " / "
+ String.valueOf(eventY));
invertedXY.setText(
"touched position: "
+ String.valueOf(x) + " / "
+ String.valueOf(y));
Drawable imgDrawable = ((ImageView) view).getDrawable();
Bitmap bitmap = ((BitmapDrawable) imgDrawable).getBitmap();
imgSize.setText(
"drawable size: "
+ String.valueOf(bitmap.getWidth()) + " / "
+ String.valueOf(bitmap.getHeight()));
//Limit x, y range within bitmap
if (x < 0) {
x = 0;
} else if (x > (bitmap.getWidth() - 1)) {
x = bitmap.getWidth() - 1;
}
if (y < 0) {
y = 0;
} else if (y > (bitmap.getHeight() - 1)) {
y = bitmap.getHeight() - 1;
}
int touchedRGB = bitmap.getPixel(x, y);
colorRGB.setText("touched color: " + "#" + Integer.toHexString(touchedRGB));
colorRGB.setTextColor(touchedRGB);
return true;
}
};
Task: zoom layout to fill all available space.
I created container, called it ZoomViewGroup which measures single child inside itself and sets scaleX and scaleY depending on its own size.
package sample.andrew.myapplication;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import org.jetbrains.annotations.NotNull;
/**
* Container class for single child to be zoomed to fill container.
*/
public class ZoomViewGroup extends ViewGroup
{
private static final String log = "mopo-zoom";
#SuppressWarnings("UnusedDeclaration")
public ZoomViewGroup(Context context)
{
super(context);
}
#SuppressWarnings("UnusedDeclaration")
public ZoomViewGroup(Context context, AttributeSet attrs){
super(context, attrs);
}
#SuppressWarnings("UnusedDeclaration")
public ZoomViewGroup(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
private void checkCount() {
if(getChildCount() > 1)
{
throw new IllegalStateException("ZoomViewGroup can host only one direct child");
}
}
#Override
public void addView(#NotNull View child) {
checkCount();
super.addView(child);
}
#Override
public void addView(#NotNull View child, int index) {
checkCount();
super.addView(child, index);
}
#Override
public void addView(#NotNull View child, LayoutParams params) {
checkCount();
super.addView(child, params);
}
#Override
public void addView(#NotNull View child, int index, LayoutParams params) {
checkCount();
super.addView(child, index, params);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
View child = getChildAt(0);
int fake_size = MeasureSpec.makeMeasureSpec(3000, MeasureSpec.UNSPECIFIED);
assert child != null;
assert child.getLayoutParams() != null;
if(child.getLayoutParams().width == LayoutParams.MATCH_PARENT)
{
int w = MeasureSpec.getSize(widthMeasureSpec);
int fake_w = MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY);
// if(Log.ENABLED) Log.i(log, "measure child with width " + w + "x3000");
measureChildren(fake_w, fake_size);
// if(Log.ENABLED) Log.i(log, "measured child: " + child.getMeasuredWidth() + "x" + child.getMeasuredHeight());
}
else
{
// By using fake size we will get child measured with
// wrap_content size, so we can calculate needed zoom to fit
// child in whole space
measureChildren(fake_size, fake_size);
}
int w = MeasureSpec.getSize(widthMeasureSpec);
int h = MeasureSpec.getSize(heightMeasureSpec);
// ZoomViewGroup supports only match_parent layout params for itself,
// so we don't modify income sizes and set them directly.
setMeasuredDimension(w, h);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if(Log.ENABLED) Log.i(log, "onLayout " + l + " " + t + " " + r + " " +b);
View child = getChildAt(0);
assert child != null;
if(child.getVisibility() != GONE) {
int w = r - l;
int h = b - t;
int child_w = child.getMeasuredWidth();
int child_h = child.getMeasuredHeight();
if(child_w == w && child_h == h)
{
child.layout(l, t, r, b);
}
else
{
int dx = w - child_w;
int dy = h - child_h;
if(Log.ENABLED) Log.i(log, "dx, dy " + dx + " " + dy);
int cl,ct,cr,cb;
if(dx == 0) {
cl = 0;
cr = child_w;
} else {
cl = (w-child_w)/2;
cr = cl + child_w;
}
if(dy == 0)
{
ct = 0;
cb = child_h;
}
else
{
ct = (h - child_h)/2;
cb = ct + child_h;
}
if(Log.ENABLED) Log.i(log, "set child bounds: " +
cl + " " + ct + " " + cr + " " + cb);
child.layout(cl, ct, cr, cb);
}
}
}
#Override
public void onSizeChanged(int width, int height, int oldw, int oldh) {
super.onSizeChanged(width, height, oldw, oldh);
if(Log.ENABLED) Log.i(log, "onSizeChanged " + oldw + "x" + oldh +
" -> " + width + "x" + height);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
{
View child = getChildAt(0);
if(child != null)
{
float originScaleX = child.getScaleX();
float originScaleY = child.getScaleY();
if(originScaleX != 1 || originScaleY != 1)
{
if(Log.ENABLED) Log.i(log, "scale set: " + originScaleX + " " + originScaleY);
if(Log.ENABLED) Log.i(log, "scale size: " + (int)((originScaleX*child.getMeasuredWidth())) + "x"
+ (int)(originScaleY*child.getMeasuredHeight())
+ " [" + child.getMeasuredWidth() + "x" + child.getMeasuredHeight() + "]" );
return;
}
#SuppressWarnings("ConstantConditions")
int originWidth = child.getMeasuredWidth();
int originHeight = child.getMeasuredHeight();
if(Log.ENABLED) Log.i(log, "child size: " + originWidth + " " + originHeight);
if(originWidth > 0)
{
float zoomFactorX = findZoomCoef(width, originWidth);
float zoomFactorY = findZoomCoef(height, originHeight);
if(Log.ENABLED) Log.i(log, "calc zoom [" + zoomFactorX + ", " + zoomFactorY + "]");
child.setScaleX(zoomFactorX);
child.setScaleY(zoomFactorY);
}
}
}
}
/**
* Calculates such coefficient to meet rule:
* size = (int)(sizeToZoom*coef)
* #param size
* #param sizeToZoom
* #return coef
*/
public static float findZoomCoef(int size, int sizeToZoom)
{
float zoomFactor = (size*100/sizeToZoom)/100.0f;
float step = 0.001f;
int count = 0;
do
{
int reverse_size = (int) (sizeToZoom*zoomFactor);
if(reverse_size == size)
break;
if(reverse_size < size)
{
zoomFactor += step;
}
else
{
zoomFactor -= step;
step /= 10;
}
count++;
}
while (true);
if(Log.ENABLED) Log.i(log, "calc zoom: s,c " + step + " " + count);
return zoomFactor;
}
}
This implementation works well for major count of devices except nexus 4 and nexus 5. This is first time I see this. Usually something doesn't work on samsung or sony devices but not vice versa.
Test layout to show problem:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MyActivity">
<sample.andrew.myapplication.ZoomViewGroup
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:id="#+id/test_layout">
<View android:layout_width="200dp"
android:layout_height="200dp"
android:background="#70c0"
android:id="#+id/anchor"
/>
<View android:layout_width="20dp"
android:layout_height="20dp"
android:background="#f00"
android:layout_centerInParent="true"
/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/test_anchored_layout"
android:layout_alignTop="#id/anchor"
android:layout_alignLeft="#id/anchor"
android:layout_alignRight="#id/anchor"
android:layout_alignBottom="#id/anchor">
<!--<RelativeLayout-->
<!--android:layout_width="200dp"-->
<!--android:id="#+id/test_anchored_layout"-->
<!--android:layout_height="200dp">-->
<View android:layout_width="80dp"
android:layout_height="20dp"
android:layout_centerHorizontal="true"
android:background="#00f"
/>
</RelativeLayout>
</RelativeLayout>
</sample.andrew.myapplication.ZoomViewGroup>
</RelativeLayout>
How test sample looks:
1. android studio designer/preview
2 how it supposed to be and how it works for example on all galaxy devices
3 how it looks on nexus 4 and nexus 5
I found that if test_anchored_layout uses fixed size(commented part) and not layout rules then all is ok even on nexuses.
Bug of nexuses or I something don't understand?
Full project archive:
project zipped
apk file
Try
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/test_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true" >
<View
android:id="#+id/anchor"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#70c0" />
<View
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_centerInParent="true"
android:background="#f00" />
<RelativeLayout
android:id="#+id/test_anchored_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignBottom="#id/anchor"
android:layout_alignLeft="#id/anchor"
android:layout_alignRight="#id/anchor"
android:layout_alignTop="#id/anchor" >
<!-- <RelativeLayout -->
<!-- android:layout_width="200dp" -->
<!-- android:id="#+id/test_anchored_layout" -->
<!-- android:layout_height="200dp"> -->
<View
android:layout_width="80dp"
android:layout_height="20dp"
android:layout_centerHorizontal="true"
android:background="#00f" />
</RelativeLayout>
</RelativeLayout>
I am having problem inflating my CustomView programmatically from the XML layout that I specify.
I have a CustomView which extends RelativeLayout and contains another RelativeLayout which in turns contain 2 ImageView and 1 LinearLayout. The ImageViews are arrow Icons which I put to the left and right of the parent by android:layout_alignParentLeft="true" and android:layout_alignParentRight="true" respectively, and the LinearLayout is used to fill all the space in between.
To make it clear, here is the xml layout view in the Eclipse Layout Designer, which is what I intended it to be...
If I setContentView(R.layout.my_xml_layout); directly from the Activity, everything appears as shown in Eclipse Layout Designer, however, if I inflate the R.layout.my_xml_layout from my CustomView's constructor, there is a stubborn margin to the left and right of the ImageView that cannot go away.
This is done in java code, and is problematic:
Any help will be highly appreciated! Thanks in advance!
my_xml_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true">
<ImageView
android:id="#+id/imageLeftArrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:src="#drawable/dock_leftarrow" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
</LinearLayout>
<ImageView
android:id="#+id/imageRightArrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:src="#drawable/dock_rightarrow" />
</RelativeLayout>
</RelativeLayout>
I inflate it in the CustomView's Constructor through this line:
View.inflate( mContext, R.layout.my_xml_layout, this );
My CustomView's onLayout:
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// Do nothing. Do not call the superclass method--that would start a layout pass
// on this view's children. PieChart lays out its children in onSizeChanged().
Log.e("DrawView", "DrawView.onLayout: " + l + ", " + t + ", " + r + ", " + b);
int iChildCount = this.getChildCount();
for ( int i = 0; i < iChildCount; i++ ) {
View pChild = this.getChildAt(i);
pChild.layout(0, 0, pChild.getMeasuredWidth(), pChild.getMeasuredHeight());
}
}
My CustomView's onMeasure:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Try for a width based on our minimum
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.d("DockView", "DockView.onMeasure: width: " + widthMeasureSpec + " getWidth: " + MeasureSpec.getSize(widthMeasureSpec));
Log.d("DockView", "DockView.onMeasure: height: " + heightMeasureSpec + " getHeight: " + MeasureSpec.getSize(heightMeasureSpec));
Log.d("DockView", "DockView.onMeasure: getPaddingLeft: " + getPaddingLeft() + " getPaddingRight: " + getPaddingRight());
Log.d("DockView", "DockView.onMeasure: getPaddingTop: " + getPaddingTop() + " getPaddingBottom: " + getPaddingBottom());
// http://stackoverflow.com/a/17545273/474330
int iParentWidth = MeasureSpec.getSize(widthMeasureSpec);
int iParentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(iParentWidth, iParentHeight);
int iChildCount = this.getChildCount();
for ( int i = 0; i < iChildCount; i++ ) {
View pChild = this.getChildAt(i);
this.measureChild( pChild,
MeasureSpec.makeMeasureSpec(iParentWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(iParentHeight, MeasureSpec.EXACTLY)
);
}
}
For the time being, I am resorting to a hack.
I only add the LinearLayout as subview of my CustomView. Then I manually render the two ImageViews in onDraw(Canvas c); function of my CustomView. And in order to get the LinearLayout to fit into the remaining space between the two ImageViews, I calculate the margin of the LinearLayout in my CustomView's onLayout.
horizontal_dock_view.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#android:color/white" >
<ImageView
android:id="#+id/imageLauncher"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_launcher"/>
</LinearLayout>
</RelativeLayout>
CustomView.java
/**
* Initialize the control. This code is in a separate method so that it can be
* called from both constructors.
*/
private void init() {
setWillNotDraw( false );
// Load the arrow bitmap
mArrowBitmap = ((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.dock_leftarrow)).getBitmap();
ViewGroup pRootView = (ViewGroup) View.inflate( mContext, R.layout.horizontal_dock_view, this );
Log.d("DockView", "DockView.init: " + pRootView.getClass().getCanonicalName());
mIconContainerView = (LinearLayout) ((RelativeLayout)pRootView.getChildAt(0)).getChildAt(0);
Log.d("DockView", "DockView.init: " + mIconContainerView.getClass().getCanonicalName());
// if ( mArrowBitmap != null ) {
// // set the icon container margin
// float fWidth = this.getWidth(); // View's width
// float fHeight = this.getHeight(); // View's height
// float fScale = fHeight / mArrowBitmap.getHeight();
// float fArrowWidth = mArrowBitmap.getWidth() * fScale;
// float fArrowHeight = mArrowBitmap.getHeight() * fScale;
// Log.d("DockView", "DockView.init: " + fArrowWidth + ", " + fArrowHeight );
// ((RelativeLayout.LayoutParams)mIconContainerView.getLayoutParams()).setMargins((int)fArrowWidth, 0, (int)fArrowWidth, 0);
// }
}
CustomView.onLayout:
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// Do nothing. Do not call the superclass method--that would start a layout pass
// on this view's children. PieChart lays out its children in onSizeChanged().
Log.e("DrawView", "DrawView.onLayout: " + l + ", " + t + ", " + r + ", " + b);
if ( mIconContainerView != null && mArrowBitmap != null ) {
// set the icon container margin
float fHeight = this.getHeight();
float fScale = fHeight / mArrowBitmap.getHeight();
float fArrowWidth = mArrowBitmap.getWidth() * fScale;
float fArrowHeight = mArrowBitmap.getHeight() * fScale;
Log.d("DockView", "DockView.init: " + fArrowWidth + ", " + fArrowHeight );
((RelativeLayout.LayoutParams)mIconContainerView.getLayoutParams()).setMargins((int)fArrowWidth, 0, (int)fArrowWidth, 0);
this.requestLayout();
}
int iChildCount = this.getChildCount();
for ( int i = 0; i < iChildCount; i++ ) {
View pChild = this.getChildAt(i);
pChild.layout(0, 0, pChild.getMeasuredWidth(), pChild.getMeasuredHeight());
}
}
CustomView.onDraw
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// mBgColor = Color.CYAN;
// Log.e("DockView", "DockView.onDraw: " + mBgColor);
Log.e("DockView", "DockView.onDraw: width: " + this.getWidth() + " height: " + this.getHeight());
// debugChildren( (RelativeLayout) ((RelativeLayout)this.getChildAt(0)).getChildAt(0) );
debugChildren( ((RelativeLayout)this.getChildAt(0)) );
// draw the background
canvas.drawColor( mBgColor );
float fWidth = this.getWidth(); // View's width
float fHeight = this.getHeight(); // View's height
{
// draw the dock
float fTop = (2 * fHeight) / 3.0f;
Shader shader = new LinearGradient( 0, fTop, 0, fHeight, mDockTopGradientColor, mDockBottomGradientColor, TileMode.CLAMP );
Paint paint = new Paint();
paint.setShader(shader);
canvas.drawRect( new RectF( 0, fTop, fWidth, fHeight ), paint );
}
// moved to onLayout
// if ( mIconContainerView != null && mArrowBitmap != null ) {
// // set the icon container margin
// float fScale = fHeight / mArrowBitmap.getHeight();
// float fArrowWidth = mArrowBitmap.getWidth() * fScale;
// float fArrowHeight = mArrowBitmap.getHeight() * fScale;
// Log.d("DockView", "DockView.init: " + fArrowWidth + ", " + fArrowHeight );
// ((RelativeLayout.LayoutParams)mIconContainerView.getLayoutParams()).setMargins((int)fArrowWidth, 0, (int)fArrowWidth, 0);
// this.requestLayout();
// }
if ( mArrowBitmap != null ) {
// draw the arrow
// canvas.drawBitmap(mArrowBitmap, 0, 0, null);
float fScale = fHeight / mArrowBitmap.getHeight();
float fDrawnWidth = mArrowBitmap.getWidth() * fScale;
float fDrawnHeight = mArrowBitmap.getHeight() * fScale;
// float fLeft = fWidth - fDrawnWidth;
// float fTop = 0.0f;
// float fRight = fWidth;
// float fBottom = fDrawnHeight;
// Log.d("DockView", "DockView.onDraw: (" + fLeft + ", " + fTop + ", " + fRight + ", " + fBottom + ")");
canvas.drawBitmap(mArrowBitmap, null, new RectF(0, 0, fDrawnWidth, fDrawnHeight), null); // Left arrow
Log.d("DockView", "DockView.onDraw: (" + 0 + ", " + 0 + ", " + fDrawnWidth + ", " + fDrawnHeight + ")");
canvas.save();
canvas.scale(-1,1);
canvas.translate(-fWidth, 0);
// canvas.drawBitmap(mArrowBitmap, null, new RectF(fLeft, fTop, fRight, fBottom), null);
canvas.drawBitmap(mArrowBitmap, null, new RectF(0, 0, fDrawnWidth, fDrawnHeight), null); // Right arrow, flipped
canvas.restore();
}
}
i am trying to send data in a listview when a button is clicked.
However my listview show 2 row on at once one full row and one partial row . Is there a way i can determine which row is showing partial and which is showing fully.
I am able to get the index that is showing only. is there another approach ?
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == SCROLL_STATE_IDLE){
Rect r = new Rect ();
View child = recordListview.getChildAt(view.getFirstVisiblePosition()); // first visible child
if (child == null)
return;
double height = child.getHeight () * 1.0;
recordListview.getChildVisibleRect (child, r, null);
Log.d("Visible1 ", view.getFirstVisiblePosition() + " " + height + " " + r.height() );
if (Math.abs (r.height ()) < height / 2.0) {
// show next child
recordListview.smoothScrollToPosition(view.getFirstVisiblePosition()+1);
Log.d("Visible1 Location", view.getFirstVisiblePosition() +1+ "");
}
else {
recordListview.smoothScrollToPosition(view.getFirstVisiblePosition());
Log.d("Visible1 Location", view.getFirstVisiblePosition()+ "");
}
}
}
});
Seems You've understood documentation of getChildVisibleRect() incorrectly.
It mentions:
r The input rectangle, defined in the child coordinate system. Will
be overwritten to contain the resulting visible rectangle, expressed
in global (root) coordinates
So, if You're providing empty rectangle in the child coordinate then it can be translated only into empty visible rectagle, right?
For me this code seems to work:
recordListview.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(final AbsListView view, final int scrollState) {
if (scrollState == SCROLL_STATE_IDLE) {
final View child = recordListview.getChildAt(view.getFirstVisiblePosition());
if (child == null) {
return;
}
final Rect r = new Rect (0, 0, child.getWidth(), child.getHeight());
final double height = child.getHeight () * 1.0;
recordListview.getChildVisibleRect(child, r, null);
Log.d("Visible1 ", view.getFirstVisiblePosition() + " " + height + " " + r.height());
if (Math.abs (r.height ()) < height / 2.0) {
// show next child
recordListview.smoothScrollToPosition(view.getFirstVisiblePosition()+1);
Log.d("Visible1 Location", view.getFirstVisiblePosition() +1+ "");
} else {
recordListview.smoothScrollToPosition(view.getFirstVisiblePosition());
Log.d("Visible1 Location", view.getFirstVisiblePosition()+ "");
}
}
}
#Override
public void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount, final int totalItemCount) {
// nothing to do here
}
});
Regarding initial question about determining which view is visible fully and which is not, I would suggest to use the following code:
#Override
public void onScrollStateChanged(final AbsListView view, final int scrollState) {
if (scrollState == SCROLL_STATE_IDLE) {
final int firstVisiblePosition = view.getFirstVisiblePosition();
View child = recordListview.getChildAt(firstVisiblePosition);
if (child == null) {
return;
}
if (mListItemsOnScreen == 0) {
// number of total visible items, including items which are not fully visible
mListItemsOnScreen = (int) Math.ceil(((double)recordListview.getHeight()) / (child.getHeight() + recordListview.getDividerHeight()));
}
final Rect r = new Rect(0, 0, child.getWidth(), child.getHeight());
final double height = child.getHeight();
recordListview.getChildVisibleRect(child, r, null);
Log.d("Visible1", " items till " + firstVisiblePosition + " are not visible");
// Check top item
Log.d("Visible1", firstVisiblePosition + " is visible " + (r.height() >= height ? " fully" : "partially"));
// check bottom item
child = recordListview.getChildAt(firstVisiblePosition + mListItemsOnScreen);
if (child != null) {
r.set(0, 0, child.getWidth(), child.getHeight());
recordListview.getChildVisibleRect(child, r, null);
Log.d("Visible1", " items from " + firstVisiblePosition + " till " + (firstVisiblePosition + mListItemsOnScreen) + " are fully visible");
Log.d("Visible1", (firstVisiblePosition + mListItemsOnScreen) + " is visible " + (r.height() >= height ? " fully" : "partially"));
} else {
Log.d("Visible1", " items from " + firstVisiblePosition + " till " + (firstVisiblePosition + mListItemsOnScreen) + " are fully visible");
Log.d("Visible1", (firstVisiblePosition + mListItemsOnScreen) + " is invisible ");
}
}
}
I want to display the labels on pie chart items..please guide me..
Main.java:-
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
PieDetailsItem item;
int maxCount=0;
int itemCount=0;
int items[]={2,6,0};
int colors[]={-6777216,-16776961,-16711681};
String itemslabel[]={" vauesr ur 100"," vauesr ur 200"," vauesr ur 300"};
for(int i=0;i<items.length;i++)
{
itemCount=items[i];
item=new PieDetailsItem();
item.count=itemCount;
item.label=itemslabel[i];
item.color=colors[i];
piedata.add(item);
maxCount=maxCount+itemCount;
}
int size=155;
int BgColor=0xffa11b1;
Bitmap mBaggroundImage=Bitmap.createBitmap(size,size,Bitmap.Config.ARGB_8888);
View_PieChart piechart=new View_PieChart(this);
piechart.setLayoutParams(new LayoutParams(size,size));
piechart.setGeometry(size, size, 2, 2, 2, 2, 2130837504);
piechart.setSkinparams(BgColor);
piechart.setData(piedata, maxCount);
piechart.invalidate();
piechart.draw(new Canvas(mBaggroundImage));
piechart=null;
ImageView mImageView=new ImageView(this);
mImageView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT));
mImageView.setBackgroundColor(BgColor);
mImageView.setImageBitmap(mBaggroundImage);
LinearLayout finalLayout=(LinearLayout)findViewById(R.id.pie_container);
finalLayout.addView(mImageView);
}
View_piechart.java:-
public View_PieChart(Context context) {
super(context);
Log.w(" single cons ", " single cons");
}
public View_PieChart(Context context, AttributeSet attr) {
super(context, attr);
Log.w(" double cons ", " double cons");
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mState != IS_READY_TO_DRAW) {
return;
}
canvas.drawColor(mBgcolor);
mBagpaints.setAntiAlias(true);
mBagpaints.setStyle(Paint.Style.FILL);
mBagpaints.setColor(0x88FF0000);
mBagpaints.setStrokeWidth(0.0f);
mLinePaints.setAntiAlias(true);
mLinePaints.setColor(0xff000000);
mLinePaints.setStrokeWidth(3.0f);
mLinePaints.setStyle(Paint.Style.STROKE);
RectF mOvals = new RectF(mGapleft, mGapTop, mWidth - mGapright, mHeight
- mGapBottm);
mStart = START_INC;
PieDetailsItem item;
for (int i = 0; i < mdataArray.size(); i++) {
item = (PieDetailsItem) mdataArray.get(i);
mBagpaints.setColor(item.color);
mSweep = (float) 360* ((float) item.count / (float) mMaxConnection);
canvas.drawArc(mOvals, mStart, mSweep, true, mBagpaints);
canvas.drawArc(mOvals, mStart, mSweep, true, mLinePaints);
mStart = mStart + mSweep;
}
mState = IS_DRAW;
}
public void setGeometry(int width, int height, int gapleft, int gapright,
int gaptop, int gapbottom, int overlayid) {
mWidth = width;
mHeight = height;
mGapleft = gapleft;
mGapright = gapright;
mGapBottm = gapbottom;
mGapTop = gaptop;
}
public void setSkinparams(int bgcolor) {
Log.w(" Set bg color : ", bgcolor + "");
mBgcolor = bgcolor;
}
public void setData(List<PieDetailsItem> data, int maxconnection) {
mdataArray = data;
mMaxConnection = maxconnection;
Log.w(" Max Connection ", maxconnection + " " + " Adataarray :"
+ data.toString());
mState = IS_READY_TO_DRAW;
}
public void setState(int state) {
mState = state;
}
public int getColorValues(int index) {
if (mdataArray == null) {
return 0;
}
else if (index < 0)
return ((PieDetailsItem) mdataArray.get(0)).color;
else if (index > mdataArray.size())
return ((PieDetailsItem) mdataArray.get(mdataArray.size() - 1)).color;
else
return ((PieDetailsItem) mdataArray.get(mdataArray.size() - 1)).color;
}
main.xml
>
<LinearLayout android:id="#+id/pie_container"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
</LinearLayout>
You're not going to use a TextView, you're just going to draw the text right on top of your pies.
refer to this:
Android Canvas.drawText
Post xml layouts for the views that you want.