i made custom Image View, where a simple line is drawn. This drawing is triggeed by a button.
When triggered the onClicklistener isnt working anymore. But on the screen the button remains clickable, the buttons changes to a blueish color when clicked (default Android button). I think the problem might be in the "drawLine()" when the "setContentView(R.layout.activity_main);" is called, but not sure why and how to get rid of it. Hope you can help.
MainActivity.java
public class MainActivity extends ActionBarActivity implements OnTouchListener,
OnClickListener {
private int number;
private Handler handler;
private boolean Running = true;
private int endX = 50;
private int endY = 500;
private int startX = 50;
private int startY = 50;
private int frames = 25;
ImageView Line01;
Button buttonLineDrawer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Line01 = (ImageView) findViewById(R.id.Line01);
Line01.setVisibility(View.GONE);
Button buttonLineDrawer;
buttonLineDrawer = (Button) findViewById(R.id.buttonLineDrawer);
buttonLineDrawer.setOnClickListener(this);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
return false;
}
#Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "klick", Toast.LENGTH_LONG)
.show();
Running = true;
number = 0;
drawLine();
}
public void drawLine() {
Line01.setVisibility(View.VISIBLE);
handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
while (Running) {
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.post(new Runnable() {
#Override
public void run() {
int coordX = ((((endX - startX) / frames) * number))
+ startX;
int coordY = ((((endY - startY) / frames) * number))
+ startY;
number += 1;
CustomDraw.setCoordinates(startX, startY, coordX,
coordY);
setContentView(R.layout.activity_main);
if ((coordX - endX) == 0 && coordY - endY == 0) {
Running = false;
}
}
});
}
}
};
new Thread(runnable).start();
}}
activity_main.xml
<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"
android:background="#FF8800"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="de.trialar.linedrawer.MainActivity" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
<Button
android:id="#+id/buttonLineDrawer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
<de.trialar.linedrawer.CustomDraw
android:id="#+id/Line01"
android:layout_width="400dp"
android:layout_height="400dp" />
</LinearLayout></RelativeLayout>
CustomDraw.java
public class CustomDraw extends ImageView {
Paint paint = new Paint();
private static int endX= 500;
private static int endY= 500;
private static int startX= 50;
private static int startY= 50;
static Context context;
public CustomDraw(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
}
public CustomDraw(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomDraw(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(Color.BLACK);
paint.setStrokeWidth(10);
canvas.drawLine(startX, startY, endX, endY, paint);
}
public static void setCoordinates(int startX, int startY, int endX, int endY) {
System.out.println("SetCoordinates");
CustomDraw.endX = endX;
CustomDraw.endY = endY;
CustomDraw.startX = startX;
CustomDraw.startY = startY;
}}
i think i found the problem: when you start the handler.run() you set a new Layout:
public void run() {
int coordX = ((((endX - startX) / frames) * number)) + startX;
int coordY = ((((endY - startY) / frames) * number)) + startY;
number += 1;
CustomDraw.setCoordinates(startX, startY, coordX, coordY);
//maybe this is wrong
setContentView(R.layout.activity_main);
//try instead:
CustomDraw.invalidate();
if ((coordX - endX) == 0 && coordY - endY == 0) {
Running = false;
}
}
i don't get why you set a new layout... i guess you want to repaint the button... that's why i added a 'repaint'-line in that code-piece above
Related
I'm currently programming my first java game in Android studio. I want to do a setContentview of my activity layout and my gameView. I want to print on screen some controls to move my character, but all I get is my draw() method on screen.
My game activity
public class GameActivity extends AppCompatActivity {
private GameView gameView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
//setContentView(R.layout.activity_game);
gameView = new GameView(this);
setContentView(gameView);
}
#Override
protected void onPause() {
super.onPause();
gameView.pause();
}
#Override
protected void onResume() {
super.onResume();
gameView.resume();
}
}
My game view
public class GameView extends SurfaceView implements Runnable{
SurfaceHolder surfaceHolder;
String WALK_DIR = "";
private final long NS_perSec = 1000000000;
private int currentFrame = 0;
private static long FPS = 0;
private long timePassed;
public Bitmap character, arrow_up, arrow_down, arrow_left, arrow_right;
private Thread thread;
private boolean isPlaying;
private boolean isMoving = false;
private final float WALK_SPD = 100;
float startPositionX, startPositionY;
float arrowX = 45, arrowY = 30;
Paint paint;
//Sprite Animation
private int frameWidth = 100;
private int frameHeight = 400;
private int frameCount = 4;
public Rect frameToDraw = new Rect(
0,0, frameWidth, frameHeight/frameCount);
RectF whereToDraw = new RectF(
0, 0,
startPositionX + frameWidth,
frameHeight);
public static float screenRatioX, screenRatioY;
private long lastFrameChangeTime = 0l;
public GameView(Context context) {
super(context);
surfaceHolder = getHolder();
Display display = ((Activity)context).getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
screenRatioX = size.x;
screenRatioY = size.y;
startPositionX = screenRatioX/2 - frameWidth/2;
startPositionY = screenRatioY/2 - frameHeight/8;
paint = new Paint();
character = BitmapFactory.decodeResource(this.getResources(), R.drawable.character_sprite);
arrow_right = BitmapFactory.decodeResource(this.getResources(), R.drawable.arrow_right);
arrow_down = BitmapFactory.decodeResource(this.getResources(), R.drawable.arrow_down);
arrow_left = BitmapFactory.decodeResource(this.getResources(), R.drawable.arrow_left);
arrow_up = BitmapFactory.decodeResource(this.getResources(), R.drawable.arrow_up);
character = Bitmap.createScaledBitmap(character,frameWidth * frameCount,frameHeight,false);
arrow_right = Bitmap.createScaledBitmap(arrow_right,(int)arrowX,(int)arrowY,false);
arrow_down = Bitmap.createScaledBitmap(arrow_down,(int)arrowY,(int)arrowX,false);
arrow_left = Bitmap.createScaledBitmap(arrow_left,(int)arrowX,(int)arrowY,false);
arrow_up = Bitmap.createScaledBitmap(arrow_up,(int)arrowY,(int)arrowX,false);
}
#Override
public void run() {
isPlaying = true;
while (isPlaying){
long startLoop = System.nanoTime();
update();
draw();
sleep();
timePassed = System.nanoTime() - startLoop;
if(timePassed >= 1){
FPS = (long) (NS_perSec / timePassed);
}
}
}
private void update(){
if(isMoving) {
startPositionX = startPositionX + (WALK_SPD / FPS);
if (startPositionX > screenRatioX){
startPositionY += (int) frameHeight;
startPositionX = 10;
}
if (startPositionY + frameHeight > screenRatioY){
startPositionY = 10;
}
}
}
private void draw(){
if (surfaceHolder.getSurface().isValid()){
Canvas canvas = getHolder().lockCanvas();
canvas.drawColor(Color.WHITE);
// Choose the brush color for drawing
paint.setColor(Color.argb(255, 249, 129, 0));
// Make the text a bit bigger
paint.setTextSize(45);
canvas.drawText("FPS:" + FPS, 20, 40, paint);
whereToDraw.set((int)startPositionX, (int)startPositionY,(int)startPositionX + frameWidth,(int)startPositionY + frameHeight/frameCount);
getCurrentFrame();
canvas.drawBitmap(character, frameToDraw, whereToDraw, paint);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
public void getCurrentFrame(){
long time = System.nanoTime();
if(isMoving) {
if ( time > lastFrameChangeTime + NS_perSec/10) {
lastFrameChangeTime = time;
currentFrame++;
if (currentFrame >= frameCount) {
currentFrame = 0;
}
}
frameToDraw.left = currentFrame * frameWidth;
frameToDraw.right = frameToDraw.left + frameWidth;
frameToDraw.top = 3 * frameHeight/4;
frameToDraw.bottom = frameToDraw.top + frameHeight/4;
}
}
private void sleep(){
try {
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void resume(){
isPlaying = true;
thread = new Thread(this);
thread.start();
}
public void pause(){
isPlaying = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
Log.e("ERROR", "Joining Thread");
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
//return super.onTouchEvent(event);
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
isMoving = true;
break;
case MotionEvent.ACTION_UP:
isMoving = false;
break;
}
return true;
}
}
game activity xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".GameActivity">
<ImageView
android:id="#+id/arrow_left"
android:layout_width="45dp"
android:layout_height="30dp"
android:translationZ="100dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.861"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.858"
app:srcCompat="#drawable/arrow_left" />
<ImageView
android:id="#+id/arrow_right"
android:layout_width="45dp"
android:layout_height="30dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.95"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.858"
app:srcCompat="#drawable/arrow_right" />
<ImageView
android:id="#+id/arrow_down"
android:layout_width="30dp"
android:layout_height="45dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.897"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.956"
app:srcCompat="#drawable/arrow_down" />
<ImageView
android:id="#+id/arrow_up"
android:layout_width="30dp"
android:layout_height="45dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.897"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.789"
app:srcCompat="#drawable/arrow_up" />
</androidx.constraintlayout.widget.ConstraintLayout>
What I got in my layout in game activity
What I get in my screen when I run the game
I want the controls to be in the right bottom corner of the screen.
I created a custom view to draw some shapes on it but my problem is that nothing is showed on the view. I get no error messages or crash. My custom class looks the like:
public class DrawableView extends View {
private Paint mPaint;
private MyDrawable mDrawableObj; // object to draw
private RectF mRectBounds;
public DrawableView(Context context) {
super(context);
init();
}
public DrawableView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DrawableView(Context context, AttributeSet attrs, int defStyleAttr){
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int w = MeasureSpec.getSize(widthMeasureSpec);
int h = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(w, h);
}
public void setDrawableObj(MyDrawable obj, boolean forcePrepare) {
final boolean needPrepare = forcePrepare || (mDrawableObj != null) ;
mDrawableObj = obj;
mDrawableObj.setView(this);
if (needPrepare) {
prepareObject();
invalidate();
}
}
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Paint.Style.STROKE);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
final int padLeft = getPaddingLeft();
final int padTop = getPaddingTop();
final float xpad = (float) (padLeft + getPaddingRight());
final float ypad = (float) (padTop + getPaddingBottom());
final float ww = (float) w - xpad - 1;
final float hh = (float) h - ypad - 1;
mRectBounds = new RectF(0, 0, ww, hh);
mRectBounds.offsetTo(padLeft, padTop);
prepareObject();
}
public void prepareObject() {
if (mDrawableObj != null)
mDrawableObj.prepare(mRectBounds);
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mDrawableObj != null)
mDrawableObj.draw(canvas, mRectBounds);
else {
canvas.drawRect(mRectBounds, mPaint);
}
}
My layout file is the following:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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:id="#+id/mainLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.hello.myApp.Draw">
<DrawableView.DrawableView
android:id="#+id/drawtest_drawableView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#+id/toolbar"
app:layout_constraintLeft_toRightOf="#+id/leftMenu"
android:padding="4dp" />
</android.support.constraint.ConstraintLayout>
Do you have any idea why I have just a white screen?
use this
public void init() {
if (isInEditMode())
return;
//code
//...
}
I copy/pasted your code into my IDE and ran it (though I had to create a fake MyDrawable interface to satisfy the compiler). I'm just putting an instance of DrawableView into a LinearLayout and I see it drawing just fine.
My layout xml:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/myLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.stackoverflow.DrawableView
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="12dp"/>
</LinearLayout>
What I see:
It makes sense that I see only the blue rectangle, because this is your onDraw() implementation:
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mDrawableObj != null)
mDrawableObj.draw(canvas, mRectBounds);
else {
canvas.drawRect(mRectBounds, mPaint);
}
}
Given that setDrawableObj(MyDrawable obj, boolean forcePrepare) is never called in the code you posted, the else is always what's being executed/drawn.
I have been studying custom views recently and trying to make one specific example work out from a tutorial located here: http://flavienlaurent.com/blog/2013/08/28/each-navigation-drawer-hides-a-viewdraghelper/
I will add the relevant code to prevent information loss from dead link. The author of the tutorial explains how to create a ViewDragHelper similar to YouTube's android app player behavior and these are the codes he provided
activity_main.xml
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="#+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:tag="list"
/>
<com.example.vdh.YoutubeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/youtubeLayout"
android:orientation="vertical"
android:visibility="visible">
<TextView
android:id="#+id/viewHeader"
android:layout_width="match_parent"
android:layout_height="128dp"
android:fontFamily="sans-serif-thin"
android:textSize="25sp"
android:tag="text"
android:gravity="center"
android:textColor="#android:color/white"
android:background="#AD78CC"/>
<TextView
android:id="#+id/viewDesc"
android:tag="desc"
android:textSize="35sp"
android:gravity="center"
android:text="Loreum Loreum"
android:textColor="#android:color/white"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF00FF"/>
</com.example.vdh.YoutubeLayout>
YoutubeLayout.java
public class YoutubeLayout extends ViewGroup {
private final ViewDragHelper mDragHelper;
private View mHeaderView;
private View mDescView;
private float mInitialMotionX;
private float mInitialMotionY;
private int mDragRange;
private int mTop;
private float mDragOffset;
public YoutubeLayout(Context context) {
this(context, null);
}
public YoutubeLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
#Override
protected void onFinishInflate() {
mHeaderView = findViewById(R.id.viewHeader);
mDescView = findViewById(R.id.viewDesc);
}
public YoutubeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mDragHelper = ViewDragHelper.create(this, 1f, new DragHelperCallback());
}
public void maximize() {
smoothSlideTo(0f);
}
boolean smoothSlideTo(float slideOffset) {
final int topBound = getPaddingTop();
int y = (int) (topBound + slideOffset * mDragRange);
if (mDragHelper.smoothSlideViewTo(mHeaderView, mHeaderView.getLeft(), y)) {
ViewCompat.postInvalidateOnAnimation(this);
return true;
}
return false;
}
private class DragHelperCallback extends ViewDragHelper.Callback {
#Override
public boolean tryCaptureView(View child, int pointerId) {
return child == mHeaderView;
}
#Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
mTop = top;
mDragOffset = (float) top / mDragRange;
mHeaderView.setPivotX(mHeaderView.getWidth());
mHeaderView.setPivotY(mHeaderView.getHeight());
mHeaderView.setScaleX(1 - mDragOffset / 2);
mHeaderView.setScaleY(1 - mDragOffset / 2);
mDescView.setAlpha(1 - mDragOffset);
requestLayout();
}
#Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
int top = getPaddingTop();
if (yvel > 0 || (yvel == 0 && mDragOffset > 0.5f)) {
top += mDragRange;
}
mDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top);
}
#Override
public int getViewVerticalDragRange(View child) {
return mDragRange;
}
#Override
public int clampViewPositionVertical(View child, int top, int dy) {
final int topBound = getPaddingTop();
final int bottomBound = getHeight() - mHeaderView.getHeight() - mHeaderView.getPaddingBottom();
final int newTop = Math.min(Math.max(top, topBound), bottomBound);
return newTop;
}
}
#Override
public void computeScroll() {
if (mDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
if (( action != MotionEvent.ACTION_DOWN)) {
mDragHelper.cancel();
return super.onInterceptTouchEvent(ev);
}
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
mDragHelper.cancel();
return false;
}
final float x = ev.getX();
final float y = ev.getY();
boolean interceptTap = false;
switch (action) {
case MotionEvent.ACTION_DOWN: {
mInitialMotionX = x;
mInitialMotionY = y;
interceptTap = mDragHelper.isViewUnder(mHeaderView, (int) x, (int) y);
break;
}
case MotionEvent.ACTION_MOVE: {
final float adx = Math.abs(x - mInitialMotionX);
final float ady = Math.abs(y - mInitialMotionY);
final int slop = mDragHelper.getTouchSlop();
if (ady > slop && adx > ady) {
mDragHelper.cancel();
return false;
}
}
}
return mDragHelper.shouldInterceptTouchEvent(ev) || interceptTap;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
mDragHelper.processTouchEvent(ev);
final int action = ev.getAction();
final float x = ev.getX();
final float y = ev.getY();
boolean isHeaderViewUnder = mDragHelper.isViewUnder(mHeaderView, (int) x, (int) y);
switch (action & MotionEventCompat.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
mInitialMotionX = x;
mInitialMotionY = y;
break;
}
case MotionEvent.ACTION_UP: {
final float dx = x - mInitialMotionX;
final float dy = y - mInitialMotionY;
final int slop = mDragHelper.getTouchSlop();
if (dx * dx + dy * dy < slop * slop && isHeaderViewUnder) {
if (mDragOffset == 0) {
smoothSlideTo(1f);
} else {
smoothSlideTo(0f);
}
}
break;
}
}
return isHeaderViewUnder && isViewHit(mHeaderView, (int) x, (int) y) || isViewHit(mDescView, (int) x, (int) y);
}
private boolean isViewHit(View view, int x, int y) {
int[] viewLocation = new int[2];
view.getLocationOnScreen(viewLocation);
int[] parentLocation = new int[2];
this.getLocationOnScreen(parentLocation);
int screenX = parentLocation[0] + x;
int screenY = parentLocation[1] + y;
return screenX >= viewLocation[0] && screenX < viewLocation[0] + view.getWidth() &&
screenY >= viewLocation[1] && screenY < viewLocation[1] + view.getHeight();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec, heightMeasureSpec);
int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0),
resolveSizeAndState(maxHeight, heightMeasureSpec, 0));
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mDragRange = getHeight() - mHeaderView.getHeight();
mHeaderView.layout(
0,
mTop,
r,
mTop + mHeaderView.getMeasuredHeight());
mDescView.layout(
0,
mTop + mHeaderView.getMeasuredHeight(),
r,
mTop + b);
}
}
The author notes that onLayoutand onMeasure are badly written and I assume these (or one of them) might be the cause of a problem with one of the children.
For my objective, I replaced the mDescView with a Framelayout view containing the respective TextView. Both of them have their height set to match_parent and the parent (mDescView) does set its height correctly, but its children (TextView inside mDescView) ignore the parent height and stretch to fit their height equal to the screen height (or the custom view height, can't tell the difference). This is a problem because the mDescView children will never adjust their height correctly according to the parent through match_parent and I have been looking for a solution for days, but none was found and through research I couldn't find a reason for why this was happening.
This is the result of this problem, notice how the TextView height is bigger than its parent mDescView despite both having their height set to match_parent
So my problem is, how can I get the child (children) of that parent (or any parents) to match their parents' height like they should.
As an additional request, if possible, can anyone explain why the author thinks some of his methods are not the best/right ones and how they should be done correctly/better instead.
I have finally corrected this problem, although I am still unaware if this is the right way to do this. My solution was to set both views' height during onLayout
I have also set this to only run on the first time this method is called, so the specific statement only runs once and only during the first time (when firstRun is true)
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mDragRange = getHeight() - mHeaderView.getHeight();
if (firstRun) {
firstRun = false;
mDescView.getLayoutParams().height = getHeight() - mHeaderView.getMeasuredHeight();
}
mHeaderView.layout(
0,
mTop,
r,
mTop + mHeaderView.getMeasuredHeight());
mDescView.layout(
0,
mTop + mHeaderView.getMeasuredHeight(),
r,
mTop + b);
}
I created a class called ParallaxController to create a parallax effect on three different imageviews with different speeds. This class uses accelerometer sensor and moves images with different speeds in the 'x' axis. My problem is with the performance, I load images in the imageviews via an AsyncTask class - Android Developers Guide - The problem occurs when I use 3 large images in imageviews , when I move the device imageviews move very slowly.
My Codes :
-ParallaxController.java :
public class ParallaxController {
private Activity context;
private View viewBack, viewTop, viewFront;
private float backSpeed, topSpeed, frontSpeed;
private float maxMove;
private SensorManager sensorManager;
private ParallaxSensorEventListener eventListener;
public ParallaxController(Activity context, View viewBack, View viewTop, View viewFront, float backSpeed, float topSpeed, float frontSpeed, float maxMove) {
this.context = context;
this.viewBack = viewBack;
this.viewTop = viewTop;
this.viewFront = viewFront;
this.backSpeed = backSpeed;
this.topSpeed = topSpeed;
this.frontSpeed = frontSpeed;
this.maxMove = maxMove;
sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
eventListener = new ParallaxSensorEventListener();
}
public void startControl() {
sensorManager.registerListener(eventListener, sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION),
SensorManager.SENSOR_DELAY_GAME);
}
public void stopControl() {
sensorManager.unregisterListener(eventListener);
}
private class ParallaxSensorEventListener implements SensorEventListener {
float lastPitch = 0;
public boolean isTablet(Context context) {
return (context.getResources().getConfiguration().screenLayout
& Configuration.SCREENLAYOUT_SIZE_MASK)
>= Configuration.SCREENLAYOUT_SIZE_LARGE;
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
#Override
public void onSensorChanged(SensorEvent sensorEvent) {
float pitch;
if (isTablet(context)) {
pitch = sensorEvent.values[2];
} else {
pitch = sensorEvent.values[1];
}
if (pitch > 45 || pitch < -45)
return;
float dp = pitch - lastPitch;
if (dp == 0)
return;
float move = (dp * maxMove) / -45.0f;
float maxSpeed = Math.max(backSpeed, Math.max(frontSpeed, topSpeed));
Display display=context.getWindowManager().getDefaultDisplay();
Point size=new Point();
display.getSize(size);
float backX = (move / maxSpeed) * backSpeed;
float topX = (move / maxSpeed) * topSpeed;
float frontX = (move / maxSpeed) * frontSpeed;
if (viewBack != null) {
float backSpace=(viewBack.getWidth()*(viewBack.getScaleX()-1))/2;
if (viewBack.getX() + backX-backSpace > 0 || viewBack.getX() + backX +viewBack.getWidth()+backSpace < size.x)
return;
} else backX=0;
if (viewFront != null) {
float frontSpace=(viewFront.getWidth()*(viewFront.getScaleX()-1))/2;
if (viewFront.getX() + frontX - frontSpace> 0 || viewFront.getX() + frontX +viewFront.getWidth()+frontSpace < size.x)
return;
} else frontX=0;
if (viewTop != null) {
float topSpace=(viewTop.getWidth()*(viewTop.getScaleX()-1))/2;
if (viewTop.getX() + topX - topSpace > 0 || viewTop.getX() + topX +viewTop.getWidth()+topSpace < size.x)
return;
} else topX=0;
if (backX!=0) viewBack.setX(viewBack.getX() + backX);
if (frontX!=0) viewFront.setX(viewFront.getX() + frontX);
if (topX!=0) viewTop.setX(viewTop.getX() + topX);
lastPitch = pitch;
Log.d("TAG",viewBack.getX()+"");
}
#Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
}
}
MainActivity.java :
public class MainActivity extends AppCompatActivity {
private ParallaxController pc;
private ImageView iv_0;
private ImageView iv_1;
private ImageView iv_2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv_0 = (ImageView) findViewById(R.id.iv_0);
iv_1 = (ImageView) findViewById(R.id.iv_1);
iv_2 = (ImageView) findViewById(R.id.iv_2);
setImage(iv_0, R.drawable.a);
setImage(iv_1, R.drawable.b);
setImage(iv_2, R.drawable.c);
scale(iv_0, 1.4f);
scale(iv_1, 1.4f);
scale(iv_2, 1.4f);
pc = new ParallaxController(this,iv_0,iv_1,iv_2, 1 , 3 , 5 , 250);
pc.startControl();
}
private void setImage(ImageView iv, int res){
BitmapWorkerTask task = new BitmapWorkerTask(iv);
task.execute(res);
}
private void scale(View view,float scale){
view.setScaleX(scale);
view.setScaleY(scale);
}
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
...
}
}
activity_main.xml
<FrameLayout 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=".MainActivity">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/iv_0"
android:layout_gravity="center"
android:scaleType="fitXY"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/iv_1"
android:layout_gravity="center" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/iv_2"
android:layout_gravity="center"/>
Note: The Activity is Always LandScape.
Thanks ... :)
In my android app, I have a crop image. So, I programming a custom view as my crop box. I can move the crop box. But my problem is how can I drag border of the crop box and change width and height of it. How can I do it?
Attr Class:
public class Attr {
public static final float CROP_BOX_START_X = 5;
public static final float CROP_BOX_START_Y = 5;
public static final float CROP_BOX_END_X = 305;
public static final float CROP_BOX_END_Y = 105;
}
CropBox Class:
public class CropBox extends View {
private Paint paint = new Paint();
public CropBox(Context context) {
super(context);
}
public CropBox(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
}
#Override
public void onDraw(Canvas canvas) {
float[][] circleXY = {
{Attr.CROP_BOX_START_X, Attr.CROP_BOX_START_Y},
{(Attr.CROP_BOX_START_X + Attr.CROP_BOX_END_X) / 2, Attr.CROP_BOX_START_Y},
{Attr.CROP_BOX_END_X, Attr.CROP_BOX_START_Y},
{Attr.CROP_BOX_START_X, Attr.CROP_BOX_END_Y},
{(Attr.CROP_BOX_START_X + Attr.CROP_BOX_END_X) / 2, Attr.CROP_BOX_END_Y},
{Attr.CROP_BOX_END_X, Attr.CROP_BOX_END_Y},
{Attr.CROP_BOX_START_X, (Attr.CROP_BOX_START_Y + Attr.CROP_BOX_END_Y) / 2},
{Attr.CROP_BOX_END_X, (Attr.CROP_BOX_START_Y + Attr.CROP_BOX_END_Y) / 2}
};
float[][] lineXY = {
{Attr.CROP_BOX_START_X, Attr.CROP_BOX_START_Y, Attr.CROP_BOX_END_X, Attr.CROP_BOX_START_Y},
{Attr.CROP_BOX_START_X, Attr.CROP_BOX_END_Y, Attr.CROP_BOX_END_X, Attr.CROP_BOX_END_Y},
{Attr.CROP_BOX_START_X, Attr.CROP_BOX_START_Y, Attr.CROP_BOX_START_X, Attr.CROP_BOX_END_Y},
{Attr.CROP_BOX_END_X, Attr.CROP_BOX_START_Y, Attr.CROP_BOX_END_X, Attr.CROP_BOX_END_Y}
};
paint.setColor(Color.CYAN);
paint.setStrokeWidth(1);
for(int i = 0 ; i < circleXY.length ; i++)
canvas.drawCircle(circleXY[i][0], circleXY[i][1], 5, paint);
paint.setStrokeWidth(2);
for(int i = 0 ; i < lineXY.length ; i++)
canvas.drawLine(lineXY[i][0], lineXY[i][2], lineXY[i][2], lineXY[i][3], paint);
}
}
CropTestActivity Class:
public class CropTestActivity extends Activity {
private ImageView imageView;
private CropBox cropBox;
private RelativeLayout relativeLayout;
private RelativeLayout.LayoutParams layoutParams;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.crop_test_layout);
imageView = (ImageView)findViewById(R.id.android_image);
cropBox = new CropBox(this);
relativeLayout = (RelativeLayout)findViewById(R.id.crop_test_layout);
layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.FILL_PARENT);
imageView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
public void onGlobalLayout() {
imageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
layoutParams.leftMargin = imageView.getWidth() / 2 - (int)((Attr.CROP_BOX_START_X + Attr.CROP_BOX_END_X) / 2) + imageView.getLeft();
layoutParams.topMargin = imageView.getHeight() / 2 - (int)((Attr.CROP_BOX_START_Y + Attr.CROP_BOX_END_Y) / 2) + imageView.getTop();
}
});
relativeLayout.addView(cropBox, layoutParams);
cropBox.setOnTouchListener(new Crop(imageView));
}
}
Crop Class:
public class Crop implements OnTouchListener {
private static final int NONE = 0;
private static final int BOX_DRAG = 1;
private static final int BORDER_DRAG = 2;
private int mode = NONE;
private float cropBoxStartX = Attr.CROP_BOX_START_X;
private float cropBoxStartY = Attr.CROP_BOX_START_Y;
private float cropBoxEndX = Attr.CROP_BOX_END_X;
private float cropBoxEndY = Attr.CROP_BOX_END_Y;
private ImageView imageView;
private PointF start = new PointF();
public Crop(ImageView imageView) {
this.imageView = imageView;
}
public boolean onTouch(View view, MotionEvent event) {
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams)view.getLayoutParams();
switch(event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
start.set(event.getX(), event.getY());
if(event.getX() > cropBoxStartX && event.getX() < cropBoxEndX && event.getY() > cropBoxStartY && event.getY() < cropBoxEndY)
mode = BOX_DRAG;
else if(event.getX() == cropBoxStartX || event.getX() == cropBoxEndX || event.getY() == cropBoxStartY || event.getY() == cropBoxEndY)
mode = BORDER_DRAG;
else
mode = NONE;
break;
case MotionEvent.ACTION_UP:
mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
if(mode == BOX_DRAG) {
layoutParams.leftMargin = (int)event.getX() - (int)start.x + view.getLeft();
layoutParams.topMargin = (int)event.getY() - (int)start.y + view.getTop();
while(layoutParams.topMargin + 5 < imageView.getTop())
layoutParams.topMargin++;
while(layoutParams.leftMargin + (cropBoxEndX - cropBoxStartX + 5) > imageView.getRight())
layoutParams.leftMargin--;
while(layoutParams.topMargin + (cropBoxEndY - cropBoxStartY + 5) > imageView.getBottom())
layoutParams.topMargin--;
while(layoutParams.leftMargin + 5 < imageView.getLeft())
layoutParams.leftMargin++;
}
else if(mode == BORDER_DRAG) {
}
break;
}
view.setLayoutParams(layoutParams);
return true;
}
}
Layout XML:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/crop_test_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ImageView
android:id="#+id/android_image"
android:src="#drawable/android"
android:layout_width="fill_parent"
android:layout_height="300dp"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:layout_gravity="center"
android:scaleType="fitXY"
android:contentDescription="#string/android_image_description" >
</ImageView>
</RelativeLayout>
Before Resize:
After Resize:
Thanks for your help.
Following is the solution,
Modified onCreate from Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.crop_test_layout);
imageView = (ImageView)findViewById(R.id.android_image);
cropBox = new CropBox(this, imageView);
relativeLayout = (RelativeLayout)findViewById(R.id.crop_test_layout);
relativeLayout.addView(cropBox);
}
Modified CropBox class:
public class CropBox extends View {
private static final int CROP_BOX_START_X = 5;
private static final int CROP_BOX_START_Y = 5;
private static final int CROP_BOX_END_X = 305;
private static final int CROP_BOX_END_Y = 105;
private static final int DRAG_SQUARE = 75;
public ImageView mImageView;
boolean mIsFirstClick = false;
private Paint paint = new Paint();
private Rect mRect;
public CropBox(Context context, ImageView aBaseView) {
super(context);
mImageView = aBaseView;
mRect = new Rect(CROP_BOX_START_X, CROP_BOX_START_Y, CROP_BOX_END_X, CROP_BOX_END_Y);
setOnTouchListener(new Crop());
}
public CropBox(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
}
#Override
public void onDraw(Canvas canvas) {
paint.setStrokeWidth(2);
paint.setColor(Color.CYAN);
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(mRect, paint);
canvas.drawLine(mRect.right-DRAG_SQUARE, mRect.bottom-DRAG_SQUARE,
mRect.right, mRect.bottom-DRAG_SQUARE, paint);
canvas.drawLine(mRect.right-DRAG_SQUARE, mRect.bottom-DRAG_SQUARE,
mRect.right-DRAG_SQUARE, mRect.bottom, paint);
}
class Crop implements OnTouchListener {
private static final int NONE = 0;
private static final int BOX_DRAG = 1;
private static final int BORDER_DRAG = 2;
private int mode = NONE;
private PointF start = new PointF();
public boolean onTouch(View view, MotionEvent event) {
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams)view.getLayoutParams();
switch(event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
start.set(event.getX(), event.getY());
if((event.getX() <= mRect.right && event.getX() >=(mRect.right - DRAG_SQUARE))
&& (event.getY() >= mRect.top && event.getY() >=(mRect.bottom - DRAG_SQUARE))){
mode = BORDER_DRAG;
mIsFirstClick = false;
}
else if(mRect.contains((int)event.getX(), (int)event.getY())) {
mode = BOX_DRAG;
if (mIsFirstClick){
mRect = new Rect(CROP_BOX_START_X, CROP_BOX_START_Y,
CROP_BOX_END_X, CROP_BOX_END_Y);
mIsFirstClick = false;
} else {
mIsFirstClick = true;
}
}
else{
mode = NONE;
mIsFirstClick = true;
}
break;
case MotionEvent.ACTION_UP:
mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
mIsFirstClick = false;
if(mode == BOX_DRAG) {
layoutParams.leftMargin = (int)event.getX() - (int)start.x + view.getLeft();
layoutParams.topMargin = (int)event.getY() - (int)start.y + view.getTop();
}
else if(mode == BORDER_DRAG) {
if (event.getX() > view.getLeft() && event.getY() > view.getTop()){
mRect.right = (int) event.getX();
mRect.bottom = (int) event.getY();
}
}
while(layoutParams.topMargin + 5 < mImageView.getTop())
layoutParams.topMargin++;
while(layoutParams.leftMargin + mRect.right > mImageView.getRight())
layoutParams.leftMargin--;
while(layoutParams.topMargin + mRect.bottom > mImageView.getBottom())
layoutParams.topMargin--;
while(layoutParams.leftMargin + 5 < mImageView.getLeft())
layoutParams.leftMargin++;
break;
}
view.setLayoutParams(layoutParams);
invalidate();
return true;
}
}
}
Some points I would like to mention.
Merged Attr and Crop in CropBox
No need of creating a rectangle from lines. You can use Rect.
Never initialize an array/object in Draw method
Added a feature: if double touched on rectangle it returns to original position
There might be some hitches about the restricting the rect in imageview. I am sure you can fix those... :)
Other than this there is another interesting way using scaling of canvas Image in Canvas with touch events
Use that class instead of Cropbox and try it.
Hope it helps..