relativelayout center rules doesn't work, only on nexuses - android

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>

Related

obtain width of HorizontalScrollView child

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

Put View Into Circular Layout Corner Android

I am made a semi circle custom layout. In which i want to put the my view into the outer surface of the semi circle. but it i not showing exactly i want.
I the below code I have override the OnLayout method of view group.
I have posted my code below:-
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int mAngleRange = 180;
final int childs = getChildCount();
float totalWeight = 0f;
for (int i = 0; i < childs; i++) {
totalWeight += 1;
}
float startAngle = 270;
final int width = getWidth();
final int height = getHeight();
for (int i = 0; i < childs; i++) {
final View child = getChildAt(i);
final LayoutParams lp = layoutParams(child);
final float angle = mAngleRange / totalWeight * 1;
final float centerAngle = startAngle + angle / 2f;
final int x;
final int y;
if (childs > 1) {
int radius = getResources().getDimensionPixelOffset(R.dimen.item_radius);
x = (int) (radius * Math.cos(Math.toRadians(centerAngle)) + 0);
y = (int) (radius * Math.sin(Math.toRadians(centerAngle))) + height / 2;
} else {
x = width / 2;
y = height / 2;
}
final int left = lp.width != LayoutParams.MATCH_PARENT ? x : 0;
final int top = lp.height != LayoutParams.MATCH_PARENT ? y : 0;
final int right = lp.width != LayoutParams.MATCH_PARENT ? x + child.getMeasuredWidth() : width;
final int bottom = lp.height != LayoutParams.MATCH_PARENT ? y + child.getMeasuredHeight() : height;
Log.d(CircularOverlayLayout.class.getName(), "left =" + left + " top =" + top + " right =" + right + " bottom = " + bottom);
child.layout(left, top, right, bottom);
startAngle += angle;
}
}
<com.trignodev.customuidemo.CircularOverlayLayout
android:id="#+id/normalWithRange"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white">
<View
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#ff0000" />
<View
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#00ff00" />
<View
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#0000ff" />
</com.trignodev.customuidemo.CircularOverlayLayout>

Rotate Video/MediaPlayer in a TextureView

I'm working with camera2 and I'm showing a preview of my photo/video after longclick in my thumbnail. Also, I'm rotating it depending of which orientation the camera had when the picture was taken. For example, if I did a picture in 90º, my preview will be also rotated 90º.
Everything is working fine, I'm using a customContainer and there I'm using onLayout and OnMeasure to create my preview depending of the size of the screen, aspect ratio and orientation. It works fine with photos. My problem appear when I try to do the same with videos, they only work in 0º.
I tried to rotate the TextureView which contain my MediaPlayer but after this my onLayout become crazy and Itś impossible find a (l,t,r,b) combination to measure it correctly.
Here is my XML:
<?xml version="1.0" encoding="utf-8"?>
<com.android.camera.ui.common.ThumbnailContainer xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/preview_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/rounded_rectangle_thumbnail_preview"
android:visibility="invisible">
<TextureView
android:id="#+id/show_video_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"/>
<ImageView
android:id="#+id/image_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:visibility="invisible"
/>
</com.android.camera.ui.common.ThumbnailContainer>
Here is my Surface code:
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Log.i(TAG, "InicializoSurface. Width: " + width + " HEIGHT:" + height);
Log.i(TAG, "InicializoSurface. Width: " + mVideoView.getMeasuredWidth() + " HEIGHT:" + mVideoView.getMeasuredHeight());
Log.i(TAG, "View transform. Width: " + mVideoView.getWidth() + " HEIGHT:" + mVideoView.getHeight());
mMediaSurface = new Surface(mVideoView.getSurfaceTexture());
initializeMediaPlayer();
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
if (mMediaPlayer != null) {
// Make sure we stop video and release resources when activity is destroyed.
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
}
return false;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
//////////
private void initializeMediaPlayer(){
mMediaPlayer = new CustomMediaPlayer();
Uri uri = Uri.parse(mCameraDataAdapter.getList().get(0).getPath());
try {
mMediaPlayer.setDataSource(mActivity, uri);
mMediaPlayer.setSurface(mMediaSurface);
mMediaPlayer.prepareAsync();
mMediaPlayer.setOnPreparedListener(mMediaPlayer);
mMediaPlayer.setOnCompletionListener(mMediaPlayer);
} catch (IOException e) {
e.printStackTrace();
}
}
///////////
mVideoView.setVisibility(View.VISIBLE);
// mVideoView.setTranslationX(-200);
// mVideoView.setTranslationY(-200);
Log.i(TAG, "X: " + mVideoView.getX() + "Y: " + mVideoView.getY());
if (mVideoView.isAvailable()) {
onSurfaceTextureAvailable(mVideoView.getSurfaceTexture(), mVideoView.getWidth(), mVideoView.getHeight());
}
if (mMediaPlayer == null) {
initializeMediaPlayer();
}
// mMediaPlayer.mVideoHolder = mVideoView.getHolder();
// mMediaPlayer.setDisplay(mMediaPlayer.mVideoHolder);
if (mMediaPrepared) {
Log.i(TAG,"Comienzo Video");
mMediaPlayer.start();
}
Finally here is my onMeasure/OnLayout from my CustomView
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width;
int height;
int wantedWidth = 0;
int wantedHeight = 0;
if(mWidth == 0 && mHeight == 0 ){
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight =MeasureSpec.getSize(heightMeasureSpec);
}
width = mWidth;
height = mHeight;
if (mOrientation == 0 || mOrientation == 180) {
wantedWidth = width - (int)(mMargin * 2);
mVideo.measure(MeasureSpec.makeMeasureSpec(wantedWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec((int) (wantedWidth * mVideoAspectRatio), MeasureSpec.EXACTLY));
wantedHeight = (mViewTop.getLayoutParams().height) * 2 + (int) (wantedWidth * mAspectRatio);
} else {
Log.e(TAG, "Real Width = " + width + " real Height = " + height);
wantedHeight = width - 2 * mViewTop.getLayoutParams().height - (int)(mMargin * 2);
mVideo.measure(MeasureSpec.makeMeasureSpec(wantedHeight, MeasureSpec.EXACTLY),MeasureSpec.makeMeasureSpec((int) (wantedHeight * mAspectRatio), MeasureSpec.EXACTLY));
//
wantedWidth =(int) (wantedHeight * mAspectRatio) ;
wantedHeight = width - (int)(mMargin * 2);
}
Log.e(TAG, "onMeasure: " + wantedWidth + "x" + wantedHeight);
setMeasuredDimension(MeasureSpec.makeMeasureSpec(wantedWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(wantedHeight, MeasureSpec.EXACTLY));
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int w = getMeasuredWidth();
int h = getMeasuredHeight();
int viewHeight = mViewBottom.getMeasuredHeight();
int imageViewHeight = mImage.getMeasuredHeight();
int wantedHeight = 0;
// w = w - (int) (2 * mMargin);
if (mOrientation == 0 || mOrientation == 180) {
mVideo.layout(0,wantedHeight,w,wantedHeight + imageViewHeight);
}else{
mVideo.layout(viewHeight,0,r-viewHeight - (int) mMargin,w);
}
}
I have been looking in other post as Android MediaRecorder making rotated video and I saw that it's not possible to rotate the textureView, but I can't believe that I can rotate a image so easily and have to fight during this to rotate 90 degrees a video.
Thanks to #pskink for their comments in the post I found a solution with him. Finally I used a Matrix to rotate the Video Container(Texture View). The method that pskink give me is the next one:
private void setupMatrix(int width, int height, int degrees, boolean isHorizontal) {
Log.d(TAG, "setupMatrix for " + degrees + " degrees");
Matrix matrix = new Matrix();
//The video will be streched if the aspect ratio is in 1,5(recording at 480)
RectF src;
if (isHorizontal)
//In my case, I changed this line, because with my onMeasure() and onLayout() methods my container view is already rotated and scaled, so I need to sent the inverted params to the src.
src = new RectF(0, 0,mThumbnailContainer.getmWidth(), mThumbnailContainer.getmHeight());
else
src = new RectF(0, 0, mThumbnailContainer.getmWidth(),mThumbnailContainer.getmHeight());
RectF dst = new RectF(0, 0, width, height);
RectF screen = new RectF(dst);
Log.d(TAG, "Matrix: " + width + "x" + height);
Log.d(TAG, "Matrix: " + mThumbnailContainer.getmWidth() + "x" + mThumbnailContainer.getmHeight());
matrix.postRotate(degrees, screen.centerX(), screen.centerY());
matrix.mapRect(dst);
matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
matrix.mapRect(src);
matrix.setRectToRect(screen, src, Matrix.ScaleToFit.FILL);
matrix.postRotate(degrees, screen.centerX(), screen.centerY());
mVideoView.setTransform(matrix);
}
Finally it worked and it looks totally awesome. With this I have been able to rotate and scale any video totally dynamically depending of the screen of my device and the Aspect Ratio used for record the video or take the picture.

Add different colors in circle android

I have created a circle in custom view. I want to add 6 colors to it based on a condition. The 6 regions will keep changing based on the condition. For eg, each region can vary from 30 degrees to 90 degrees to 120 degrees.
My question is
1) How do I add 6 colors to the circle. Please note that I cannot divide the circle into 6 equal regions, thats not how it is supposed to be.
2) How do I assign the starting and ending points of the regions. For example if I want to add green color from 45degrees to 90 degrees. How do I do this?
The end product is supposed to look as below
package com.example.submission_customclock;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.BroadcastReceiver;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.CountDownTimer;
import android.os.Handler;
import android.text.format.Time;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.RemoteViews.RemoteView;
import java.util.TimeZone;
/**
* This widget display an analogic clock with two hands for hours and
* minutes.
*
* #attr ref android.R.styleable#AnalogClock_dial
* #attr ref android.R.styleable#AnalogClock_hand_hour
* #attr ref android.R.styleable#AnalogClock_hand_minute
*/
#RemoteView
public class AnalogClock extends View {
private Time mCalendar;
private static final String DEBUGTAG = "FA";
private Drawable mHourHand;
private Drawable mMinuteHand;
private Drawable mSecondHand;
private Drawable mDial;
private Drawable mDial_frame;
private Drawable mDial_center;
private int mDialWidth;
private int mDialHeight;
private boolean mAttached;
private final Handler mHandler = new Handler();
private float mMinutes;
private float mHour;
private boolean mChanged;
public AnalogClock(Context context) {
this(context, null);
}
public AnalogClock(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
Context mContext;
public AnalogClock(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
Resources r = context.getResources();
mContext=context;
Log.d(AnalogClock.DEBUGTAG,"Analog clock started");
mDial = r.getDrawable(R.drawable.clock4);
mDial_frame = r.getDrawable(R.drawable.clock_frame);
mDial_center = r.getDrawable(R.drawable.clock_dot);
mHourHand = r.getDrawable(R.drawable.hour_hand);
mMinuteHand = r.getDrawable(R.drawable.minute_hand);
mSecondHand = r.getDrawable(R.drawable.second_hand);
mCalendar = new Time();
mDialWidth = mDial.getIntrinsicWidth();
mDialHeight = mDial.getIntrinsicHeight();
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (!mAttached) {
mAttached = true;
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
getContext().registerReceiver(mIntentReceiver, filter, null, mHandler);
}
// NOTE: It's safe to do these after registering the receiver since the receiver always runs
// in the main thread, therefore the receiver can't run before this method returns.
// The time zone may have changed while the receiver wasn't registered, so update the Time
mCalendar = new Time();
// Make sure we update to the current time
onTimeChanged();
counter.start();
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mAttached) {
counter.cancel();
getContext().unregisterReceiver(mIntentReceiver);
mAttached = false;
}
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
float hScale = 1.0f;
float vScale = 1.0f;
if (widthMode != MeasureSpec.UNSPECIFIED && widthSize < mDialWidth) {
hScale = (float) widthSize / (float) mDialWidth;
}
if (heightMode != MeasureSpec.UNSPECIFIED && heightSize < mDialHeight) {
vScale = (float )heightSize / (float) mDialHeight;
}
float scale = Math.min(hScale, vScale);
Log.d(AnalogClock.DEBUGTAG,"onMeasure params: " + widthSize + " "
+ heightSize + " " + hScale + " " + vScale + " " + scale);
try {
setMeasuredDimension(resolveSizeAndState((int) (mDialWidth * scale), widthMeasureSpec, 0),
resolveSizeAndState((int) (mDialHeight * scale), heightMeasureSpec, 0));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mChanged = true;
}
boolean mSeconds=false;
float mSecond=0;
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
boolean changed = mChanged;
if (changed) {
mChanged = false;
}
boolean seconds = mSeconds;
if (seconds ) {
mSeconds = false;
}
int availableWidth = this.getMeasuredWidth();
int availableHeight = this.getMeasuredHeight();
int x = availableWidth / 2;
int y = availableHeight / 2;
final Drawable dial = mDial;
final Drawable dial_frame = mDial_frame;
final Drawable dial_dot = mDial_center;
int w = dial.getIntrinsicWidth();
int h = dial.getIntrinsicHeight();
boolean scaled = false;
// Log.d(AnalogClock.DEBUGTAG,"onDraw params: " + availableWidth +" "+ availableHeight + " " +
// x + " " + y + " " + w + " "+ h + " " + changed);
if (availableWidth < w || availableHeight < h) {
scaled = true;
//float scale = Math.min((float) availableWidth / (float) w,
// (float) availableHeight / (float) h);
canvas.save();
float scale1 = (float) 0.6;
float scale2 = (float) 0.8;
// Log.d(AnalogClock.DEBUGTAG,"scale params: " + scale1 + " " + scale2);
canvas.scale(scale1, scale2, x, y);
}
if (changed) {
//Log.d(AnalogClock.DEBUGTAG,"Bounds params: " + (x - (w / 2)) + " " + (y - (h / 2)) + " " + ( x + (w / 2)) + " " + (y + (h / 2)));
dial.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
//dial_frame.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
//Log.d(AnalogClock.DEBUGTAG,"Bounds params: " + (x - (w / 2 + w/10)) + " " + (y - (h / 2 + h/10)) + " " + ( x + (w / 2 + w/10)) + " " +
// (y + (h / 2 + h/10)));
dial_frame.setBounds(x - (w/2 + w/10), y - (h/2 + h/10), x + (w/2 + w/10), y + (h/2 + h/10));
dial_dot.setBounds(x -30 , y -20 , x + 30, y + 20);
//Log.d(AnalogClock.DEBUGTAG,"Bounds params: " + (x ) + " " + (y) + " " + ( x + (w / 2 )) + " " +
// (y + (h / 2)));
}
int radius = 0;
if(x>y)
radius=y-10;
else
radius=x-10;
Paint circlepaint;
circlepaint = new Paint();
circlepaint.
dial.draw(canvas);
dial_frame.draw(canvas);
// canvas.drawCircle(x, y, radius, circlepaint);
canvas.save();
canvas.rotate(mHour / 12.0f * 180.0f, x - 10, y - 10);
final Drawable hourHand = mHourHand;
if (changed) {
w = hourHand.getIntrinsicWidth();
h = hourHand.getIntrinsicHeight();
hourHand.setBounds(x -w/2, y - h/2 - h/4 , x + w/6, y + h/6);
}
hourHand.draw(canvas);
canvas.restore();
canvas.save();
canvas.rotate(mMinutes / 60.0f * 360.0f, x - 10, y - 10);
final Drawable minuteHand = mMinuteHand;
if (changed) {
w = minuteHand.getIntrinsicWidth();
h = minuteHand.getIntrinsicHeight();
//minuteHand.setBounds(x, y, x + (w / 2 + w/10), y + (h / 2 + w/10));
minuteHand.setBounds(x - w, y - h/2, x + w/6, y + h/6);
// Log.d(AnalogClock.DEBUGTAG,"Bounds params:x " + (x) + " y " + (y) + " w " + ( w ) + " h " +
// (h));
// Log.d(AnalogClock.DEBUGTAG,"Bounds params: " + (x - w) + " " + (y - h/2) + " " + ( x ) + " " +
// (y));
}
minuteHand.draw(canvas);
canvas.restore();
canvas.save();
canvas.rotate(mSecond, x, y);
if (seconds) {
w = mSecondHand.getIntrinsicWidth();
h = mSecondHand.getIntrinsicHeight();
// mSecondHand.setBounds(x, y, x + (w / 2 + w/10), y + (h / 2 + w/10));
mSecondHand.setBounds(x-w/6, y-h/6, x + w,y + h/2);
Log.d(AnalogClock.DEBUGTAG,"Bounds params: " + (x ) + " " + (y) + " " + ( w) + " " +
(h));
}
mSecondHand.draw(canvas);
canvas.restore();
canvas.save();
dial_dot.draw(canvas);
if (scaled) {
canvas.restore();
}
}
MyCount counter = new MyCount(10000, 1000);
public class MyCount extends CountDownTimer{
public MyCount(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
#Override
public void onFinish() {
counter.start();
}
#Override
public void onTick(long millisUntilFinished) {
mCalendar.setToNow();
int second = mCalendar.second;
mSecond=6.0f*second;
mSeconds=true;
//mChanged = true;
AnalogClock.this.invalidate();
}
}
private void onTimeChanged() {
mCalendar.setToNow();
int hour = mCalendar.hour;
int minute = mCalendar.minute;
int second = mCalendar.second;
mMinutes = minute + second / 60.0f;
mHour = hour + mMinutes / 60.0f;
mChanged = true;
}
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
String tz = intent.getStringExtra("time-zone");
mCalendar = new Time(TimeZone.getTimeZone(tz).getID());
}
onTimeChanged();
invalidate();
}
};
}

Android layout not aligned parent as instructed in java code

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();
}
}

Categories

Resources