Implementing a slider (SeekBar) in Android - android

I want to implement a slider, which is basically two lines, one vertical and one horizontal, crossing where the screen is touched. I have managed to make one but I have to issues:
The slider is not very smooth, there is a slight delay when I'm moving the finger
If I place two sliders it is not multitouch, and I'd like to use both of them simultaneously
Here is the code:
public class Slider extends View {
private Controller controller = new Controller();
private boolean initialisedSlider;
private int sliderWidth, sliderHeight;
private Point pointStart;
private Paint white;
private int mode;
final static int VERTICAL = 0, HORIZONTAL = 1, BOTH = 2;
public Slider(Context context) {
super(context);
setFocusable(true);
// TODO Auto-generated constructor stub
}
public Slider(Context context, AttributeSet attrs) {
super(context, attrs);
setFocusable(true);
pointStart = new Point();
initialisedSlider = false;
mode = Slider.BOTH;
}
#Override
protected void onDraw(Canvas canvas) {
if(!initialisedSlider) {
initialisedSlider = true;
sliderWidth = getMeasuredWidth();
sliderHeight = getMeasuredHeight();
pointStart.x = (int)(sliderWidth/2.0);
pointStart.y = (int)(sliderHeight/2.0);
controller = new Controller(pointStart, 3);
white = new Paint();
white.setColor(0xFFFFFFFF);
}
canvas.drawLine(controller.getCoordX(),0,
controller.getCoordX(),sliderHeight,
white);
canvas.drawLine(0, controller.getCoordY(),
sliderWidth, controller.getCoordY(),
white);
}
public boolean onTouchEvent(MotionEvent event) {
int eventaction = event.getAction();
int X = (int)event.getX();
int Y = (int)event.getY();
switch (eventaction) {
case MotionEvent.ACTION_DOWN:
if(isInBounds(X,Y)) {
updateController(X, Y);
}
break;
case MotionEvent.ACTION_MOVE:
if(isInBounds(X,Y)) {
updateController(X, Y);
}
break;
case MotionEvent.ACTION_UP:
if(isInBounds(X,Y)) {
updateController(X, Y);
}
break;
}
invalidate();
return true;
}
private boolean isInBounds(int x, int y) {
return ((x<=(sliderWidth)) && (x>=(0))
&& (y<=(sliderHeight)) && (y>=(0)));
}
private void updateController(int x, int y) {
switch(mode) {
case Slider.HORIZONTAL:
controller.setCoordX(x);
break;
case Slider.VERTICAL:
controller.setCoordY(y);
break;
case Slider.BOTH:
controller.setCoordX(x);
controller.setCoordY(y);
break;
}
}
private class Controller {
private int coordX, coordY;
Controller() {
}
Controller(Point point, int width) {
setCoordX(point.x);
setCoordY(point.y);
}
public void setCoordX(int coordX) {
this.coordX = coordX;
}
public int getCoordX() {
return coordX;
}
public void setCoordY(int coordY) {
this.coordY = coordY;
}
public int getCoordY() {
return coordY;
}
}
}
And the XML file:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello" />
<com.android.lasttest.Slider
android:id="#+id/slider"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center_horizontal"
android:adjustViewBounds="true"/>
<com.android.lasttest.Slider
android:id="#+id/slider"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_gravity="center_horizontal"
android:adjustViewBounds="true"/>
<com.android.lasttest.Slider
android:id="#+id/slider"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center_horizontal"
android:adjustViewBounds="true"/>
</LinearLayout>

How to implement a SeekBar
Add the SeekBar to your layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textView"
android:layout_margin="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<SeekBar
android:id="#+id/seekBar"
android:max="100"
android:progress="50"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
Notes
max is the highest value that the seek bar can go to. The default is 100. The minimum is 0. The xml min value is only available from API 26, but you can just programmatically convert the 0-100 range to whatever you need for earlier versions.
progress is the initial position of the slider dot (called a "thumb").
For a vertical SeekBar use android:rotation="270".
Listen for changes in code
public class MainActivity extends AppCompatActivity {
TextView tvProgressLabel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// set a change listener on the SeekBar
SeekBar seekBar = findViewById(R.id.seekBar);
seekBar.setOnSeekBarChangeListener(seekBarChangeListener);
int progress = seekBar.getProgress();
tvProgressLabel = findViewById(R.id.textView);
tvProgressLabel.setText("Progress: " + progress);
}
SeekBar.OnSeekBarChangeListener seekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// updated continuously as the user slides the thumb
tvProgressLabel.setText("Progress: " + progress);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
// called when the user first touches the SeekBar
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
// called after the user finishes moving the SeekBar
}
};
}
Notes
If you don't need to do any updates while the user is moving the seekbar, then you can just update the UI in onStopTrackingTouch.
See also
SeekBar Tutorial With Example In Android Studio

Android provides slider which is horizontal
http://developer.android.com/reference/android/widget/SeekBar.html
and implement OnSeekBarChangeListener
http://developer.android.com/reference/android/widget/SeekBar.OnSeekBarChangeListener.html
If you want vertical Seekbar then follow this link
http://hoodaandroid.blogspot.in/2012/10/vertical-seek-bar-or-slider-in-android.html

For future readers!
Starting from material components android 1.2.0-alpha01, you have slider component
ex:
<com.google.android.material.slider.Slider
android:id="#+id/slider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:valueFrom="20f"
android:valueTo="70f"
android:stepSize="10" />
Release notes
Material Design Spec
Docs

Related

Android custom SurfaceView orientation can't rotate from portrait to landscape

I'm a new android application engineer. I'm trying to make a vulkan rendering backend demo which use SurfaceView as a View on android Java code. I made the gles rendering backend demo using GLSurfaceView as well. On application code, I use setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) API set activity from default portrait to landscape. But it doesn't work on SurfaceView while it works on GLSurfaceView.
I use renderdoc capture the rendering result, the image is in landscape layout(the same layout as gles backend). I doubt it was's something wrong with some settings on activity or window, but can't figure it out the root cause. Could somebody help what maybe the problem is?
Here is the Java source code.
package com.example.graphicsdebugger;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.MotionEvent;
//////////////////////////////////////////////Jerome///////////////////////////////////////////////
import android.view.SurfaceHolder;
import android.view.View;
import android.view.WindowManager;
import android.util.Log;
import android.widget.CheckBox;
import android.widget.SeekBar;
import android.widget.TextView;
///////////////////////////////////////////////////////////////////////////////////////////////////
/**
* An example full-screen activity that shows and hides the system UI (i.e.
* status bar and navigation/system bar) with user interaction.
*/
public class DemoFullscreenActivity extends AppCompatActivity /*implements Serializable */{
//////////////////////////////////////////////Jerome///////////////////////////////////////////
private static final String TAG = "9527";
private static final int UNKNOWN_RENDERER = -1;
private static final int FORWARD_RENDERER = 0;
private static final int DEFERRED_RENDERER = 1;
private static final int SET_UNKNOWN = -1;
private static final int SET_MODEL = 0;
private static final int SET_SSAO = 1;
private static final int SET_SSSSS = 2;
private static final int SET_SSAO_RADIUS = 3;
private static final int SET_SSAO_BIAS = 4;
private static final int SET_SSAO_COEFFICIENT = 5;
private static final int SET_SSAO_KERNEL_SIZE = 6;
private static final int SET_BACKEND = 7;
private boolean mBackend = false;
private GLSurfaceView mGLSurfaceView;
private VKSurfaceView mVKSurfaceView;
protected static final float FLIP_DISTANCE = 50;
GestureDetector mDetector;
DemoRenderer mRender;
///////////////////////////////////////////////////////////////////////////////////////////////
private void setSSAORadiusSeekBar() {
SeekBar seekBar = (SeekBar) findViewById(R.id.seekBarRadius);
// set default value
seekBar.setProgress(5);
TextView textView = (TextView) findViewById(R.id.textRadius);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
textView.setText("Radius:" + Integer.toString(progress));
mRender.SetInt(SET_SSAO_RADIUS, progress);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {}
});
}
private void setSSAOBiasSeekBar() {
SeekBar seekBar = (SeekBar) findViewById(R.id.seekBarBias);
// set default value
seekBar.setProgress(5);
TextView textView = (TextView) findViewById(R.id.textBias);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
textView.setText("Bias:" + Integer.toString(progress));
mRender.SetInt(SET_SSAO_BIAS, progress);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {}
});
}
private void setSSAOCoefficientSeekBar() {
SeekBar seekBar = (SeekBar) findViewById(R.id.seekBarCoefficient);
// set default value
seekBar.setProgress(150);
TextView textView = (TextView) findViewById(R.id.textCoefficient);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
textView.setText("Coefficient:" + Integer.toString(progress));
mRender.SetInt(SET_SSAO_COEFFICIENT, progress);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {}
});
}
private void setSSAOKernelSizeSeekBar() {
SeekBar seekBar = (SeekBar) findViewById(R.id.seekBarKernelSize);
// set default value
seekBar.setProgress(2);
TextView textView = (TextView) findViewById(R.id.textKernelSize);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
textView.setText("Kernel Size:" + Integer.toString(progress * 8));
mRender.SetInt(SET_SSAO_KERNEL_SIZE, progress);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {}
});
}
private void setSSAOCheckBox() {
CheckBox ssao = (CheckBox) findViewById(R.id.ssao_check_box);
ssao.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
boolean checked = ((CheckBox)v).isChecked();
mRender.SetSSAO(checked);
}
});
}
private void setSSSSSCheckBox() {
CheckBox sssss = (CheckBox) findViewById(R.id.sssss_check_box);
sssss.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
boolean checked = ((CheckBox)v).isChecked();
mRender.SetSSSSS(checked);
}
});
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo_fullscreen);
//////////////////////////////////////////////Jerome///////////////////////////////////////
// 设置横屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// 隐藏导航栏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
// 隐藏应用标题栏
getSupportActionBar().hide();
setSSAOCheckBox();
setSSSSSCheckBox();
setSSAORadiusSeekBar();
setSSAOBiasSeekBar();
setSSAOCoefficientSeekBar();
setSSAOKernelSizeSeekBar();
rendererDraw();
///////////////////////////////////////////////////////////////////////////////////////////
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
//////////////////////////////////////////////Jerome///////////////////////////////////////////
private void rendererDraw() {
Bundle bundle = getIntent().getExtras();
if (bundle == null) return;
if (bundle.containsKey("Backend")) {
mBackend = bundle.getBoolean("Backend");
}
if (mBackend) { // Vulkan
Log.d("9527", "init VKSurfaceView");
mVKSurfaceView = (VKSurfaceView)findViewById(R.id.vk_surface_view);
SurfaceHolder holder = mVKSurfaceView.getHolder();
mRender = new DemoRenderer(this, holder.getSurface());
mVKSurfaceView.setRenderer(mRender);
} else { // GLES
Log.d("9527", "init GLSurfaceView");
mGLSurfaceView = (GLSurfaceView)findViewById(R.id.gl_surface_view);
mGLSurfaceView.setEGLContextClientVersion(2);
SurfaceHolder holder = mGLSurfaceView.getHolder();
mRender = new DemoRenderer(this, holder.getSurface());
mGLSurfaceView.setRenderer(mRender);
mGLSurfaceView.getAlpha();
}
mRender.SetInt(SET_SSAO_RADIUS, 5);
mRender.SetInt(SET_SSAO_BIAS, 5);
mRender.SetInt(SET_SSAO_COEFFICIENT, 150);
mRender.SetInt(SET_SSAO_KERNEL_SIZE, 2);
int renderer = UNKNOWN_RENDERER;
if (bundle.containsKey("Renderer")) {
renderer = bundle.getInt("Renderer");
mRender.SetRenderer(renderer);
}
if (bundle.containsKey("Backend")) {
mRender.SetInt(SET_BACKEND, mBackend ? 1 : 0);
}
if (renderer != DEFERRED_RENDERER) {
CheckBox ssao = (CheckBox) findViewById(R.id.ssao_check_box);
ssao.setEnabled(false);
} else if (bundle.containsKey("SSAO")) {
boolean isChecked = bundle.getBoolean("SSAO");
mRender.SetSSAO(isChecked);
CheckBox ssao = (CheckBox) findViewById(R.id.ssao_check_box);
ssao.setChecked(isChecked);
}
if (renderer != DEFERRED_RENDERER) {
CheckBox sssss = (CheckBox) findViewById(R.id.sssss_check_box);
sssss.setEnabled(false);
} else if (bundle.containsKey("SSSSS")) {
mRender.SetSSSSS(bundle.getBoolean("SSSSS"));
}
if (bundle.containsKey("Model")) {
mRender.SetModel(bundle.getInt("Model"));
}
mDetector = new GestureDetector(this, new GestureDetector.OnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
// TODO Auto-generated method stub
return false;
}
#Override
public void onShowPress(MotionEvent e) {
// TODO Auto-generated method stub
}
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
// TODO Auto-generated method stub
return false;
}
#Override
public void onLongPress(MotionEvent e) {
// TODO Auto-generated method stub
}
/**
*
* e1 The first down motion event that started the fling. e2 The
* move motion event that triggered the current onFling.
*/
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (e1.getX() - e2.getX() > FLIP_DISTANCE) {
Log.d(TAG, "Slide left...");
return true;
}
if (e2.getX() - e1.getX() > FLIP_DISTANCE) {
Log.d(TAG, "Slide right...");
return true;
}
if (e1.getY() - e2.getY() > FLIP_DISTANCE) {
Log.d(TAG, "Slide up...");
return true;
}
if (e2.getY() - e1.getY() > FLIP_DISTANCE) {
Log.d(TAG, "Slide down...");
return true;
}
Log.d(TAG, e2.getX() + " " + e2.getY());
return false;
}
#Override
public boolean onDown(MotionEvent e) {
// TODO Auto-generated method stub
return false;
}
});
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//Log.d(TAG, "===> MainActivity call dispatchTouchEvent()");
//Log.d(TAG, "===> super.dispatchTouchEvent() default return true");
//Log.d(TAG, "--------------------------------------------------");
return super.dispatchTouchEvent(ev);
}
#Override
public void onUserInteraction() {
//Log.d(TAG, "===> MainActivity call onUserInteraction()");
//Log.d(TAG, "--------------------------------------------------");
super.onUserInteraction();
}
float Finger_0_DownX = 0;
float Finger_0_DownY = 0;
float Finger_1_DownX = 0;
float Finger_1_DownY = 0;
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
int mActivePointerId =0;
int idx = event.findPointerIndex(mActivePointerId);
if (idx >=0 ) {
Finger_0_DownX = event.getX(idx);
Finger_0_DownY = event.getY(idx);
mRender.setFingerDown(0, Finger_0_DownX, Finger_0_DownY);
}
break;
case MotionEvent.ACTION_UP:
Finger_0_DownX = 0.0f;
Finger_0_DownY = 0.0f;
mRender.setFingerUp(0);
break;
default:
break;
}
switch(event.getActionMasked()) {
case MotionEvent.ACTION_POINTER_DOWN:
int mActivePointerId = 1;
int idx = event.findPointerIndex(mActivePointerId);
if (idx >=0 ) {
Finger_1_DownX = event.getX(idx);
Finger_1_DownY = event.getY(idx);
mRender.setFingerDown(1, Finger_1_DownX, Finger_1_DownY);
}
break;
case MotionEvent.ACTION_POINTER_UP:
Finger_1_DownX = 0.0f;
Finger_1_DownY = 0.0f;
mRender.setFingerUp(1);
break;
default:
break;
}
if (Finger_0_DownX != 0.0f && Finger_0_DownY != 0.0f) {
int mActivePointerId =0;
int idx = event.findPointerIndex(mActivePointerId);
if (idx >=0 ) {
mRender.setFingerPosition(0, event.getX(idx), event.getY(idx));
}
}
if (Finger_1_DownX != 0.0f && Finger_1_DownY != 0.0f) {
int mActivePointerId = 1;
int idx = event.findPointerIndex(mActivePointerId);
if (idx >=0 ) {
mRender.setFingerPosition(1, event.getX(idx), event.getY(idx));
}
}
return mDetector.onTouchEvent(event);
}
///////////////////////////////////////////////////////////////////////////////////////////////
}
Here is the activity layout.
<?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:id="#+id/frameLayout3"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/fullscreenBackgroundColor"
android:theme="#style/ThemeOverlay.GraphicsDebugger.FullscreenContainer"
tools:context=".DemoFullscreenActivity">
<android.opengl.GLSurfaceView
android:id="#+id/gl_surface_view"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<com.example.graphicsdebugger.VKSurfaceView
android:id="#+id/vk_surface_view"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<CheckBox
android:id="#+id/ssao_check_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="SSAO"
app:layout_constraintBottom_toTopOf="#+id/sssss_check_box"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.019"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.728" />
<CheckBox
android:id="#+id/sssss_check_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="272dp"
android:text="SSSSS"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.019"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="#+id/textRadius"
android:layout_width="72dp"
android:layout_height="35dp"
android:text="Radius:5"
app:layout_constraintBottom_toBottomOf="#+id/gl_surface_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.175"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.101" />
<TextView
android:id="#+id/textCoefficient"
android:layout_width="111dp"
android:layout_height="35dp"
android:text="Coefficient:150"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.419"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.101" />
<TextView
android:id="#+id/textBias"
android:layout_width="72dp"
android:layout_height="35dp"
android:text="Bias:5"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.68"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.101" />
<TextView
android:id="#+id/textKernelSize"
android:layout_width="111dp"
android:layout_height="35dp"
android:text="Kernel Size:16"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.925"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.101" />
<SeekBar
android:id="#+id/seekBarKernelSize"
android:layout_width="146dp"
android:layout_height="28dp"
android:max="8"
android:min="1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.935"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.043" />
<SeekBar
android:id="#+id/seekBarCoefficient"
android:layout_width="146dp"
android:layout_height="28dp"
android:max="300"
android:min="100"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.408"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.041" />
<SeekBar
android:id="#+id/seekBarRadius"
android:layout_width="146dp"
android:layout_height="28dp"
android:max="100"
android:min="0"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="#+id/gl_surface_view"
app:layout_constraintHorizontal_bias="0.141"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.04" />
<SeekBar
android:id="#+id/seekBarBias"
android:layout_width="146dp"
android:layout_height="28dp"
android:max="30"
android:min="0"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="#+id/gl_surface_view"
app:layout_constraintHorizontal_bias="0.675"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.041" />
</androidx.constraintlayout.widget.ConstraintLayout>
Here is the AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.graphicsdebugger">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.GraphicsDebugger">
<activity android:name=".RendererSettingsActivity"></activity>
<activity
android:name=".DemoFullscreenActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:label="#string/title_activity_demo_fullscreen"
android:theme="#style/Theme.GraphicsDebugger.Fullscreen"
android:launchMode="singleInstance"/>
<activity android:name=".RenderingEngineActivity" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
In the <activity> tag of AndroidManifest.xml, add the line android:screenOrientation="portrait".
Refer to https://www.geeksforgeeks.org/designing-the-landscape-and-portrait-mode-of-application-in-android/ add landscape layout. The layout file will be at res/layout-land/ directory. The layout xml file is the same name as the file in res/layout directory. This file name will be used in setContentView(R.layout.activity_layout_name). The chozen of landsacpe/portrait layout for the activity is set in AndroidManifest.xml parameter android:screenOrientation="value" as experimented while the value either landscape or portrait. When we choose portrait mode in AndroidManifest.xml, the corresponding activity will find the layout file in res/layout directory and setContentView. When choose landscape, it will find layout file in res/layout-land directory. That's why both in res/layout and res/layout-land directories, the layout file name should be the same, because we call the setContentView API in activity, the parameter should be both fit for file in res/layout and res/layout-land directories.
I need set my activity in landscape layout, so here in AndroidManifest.xml corresponding activity, I set android:screenOrientation="landscape".
Then Activity code will setContentView use the layout file in res/layout-land directory. In landscape layout xml file, I had two Views, one is GLSurfaceView and the other is VKSurfaceView.

Passing value from activity to view class

I have an activity in which there are 3 checkboxes in different views. These checkboxes are to pick up a color.
In the DrawingView class, I have to draw on my canvas with the color that is checked. What I want is to pass an integer value from the activity to the view class and set the color of paint accordingly. itried using getter and setters, but I get black color. I suppose this is because the color is being set in the constructor itself, and it does not change when I check any box.
Please refer this for updates in the below code
Code:
MainActivity: the color/checkbox is selected here. And the drawing is to be done in the layout of this activity itself.
carImageView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
drawingView=new DrawingView(carImageView.getContext());
drawingView=new DrawingView(carImageView.getContext(),null);
drawingView.setColor(color);
return false;
}
});
scratchesCb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if(b)
{
color=1;
chipsCb.setChecked(false);
dentsCb.setChecked(false);
}
}
});
chipsCb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if(b)
{
color=2;
scratchesCb.setChecked(false);
dentsCb.setChecked(false);
}
}
});
dentsCb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if(b)
{
color=3;
chipsCb.setChecked(false);
scratchesCb.setChecked(false);
}
}
});
}
View Class:
public DrawingView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
mPaint=new Paint();
if(color==1)
mPaint.setColor(Color.RED);
else if(color==2)
mPaint.setColor(Color.BLUE);
else if(color==3)
mPaint.setColor(Color.GREEN);
this.context=context;
mPath=new Path();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.MITER);
mPaint.setStrokeWidth(5f);
}
public void setColor(int color){
this.color=color;
}
public int getColor(){
return this.color;
}
Edit
I don't necessarily want to use the exact same code. All I want is to change paint color when a checkbox is selected to be able to draw on an image view. Any other approach is welcome.
In the MainActivity, you are creating a DrawingView that has no relationship to your image view that is displayed. So, when you change the color, you are not changing the color of the displayed image view but of the unconnected DrawingView. The image view never has a new color defined and always defaults to black.
Here is a video of a small working app based upon your most recently supplied code. Maybe all the colors shouldn't change when a new check box is clicked, but you will be able to address that issue separately.
Changes I have made to the Java code are commented as such. Changes were also made to the XML to allow your code to run in my environment, but those changes were not commented.
MainActivity.java (Updated)
public class MainActivity extends AppCompatActivity {
ImageView caricon;
int itemSelected = 0;
private DrawingView carImageView;
Bitmap bitmap;
ImageView backarrow;
TextView nextcheckinAB2;
Bitmap bmp;
public static int color;
CheckBox scratchesCb, chipsCb, dentsCb;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
carImageView = (DrawingView) findViewById(R.id.carImageView);
scratchesCb = (CheckBox) findViewById(R.id.scratchesCheckBox);
chipsCb = (CheckBox) findViewById(R.id.ChipCheckbx);
dentsCb = (CheckBox) findViewById(R.id.DentsCheckBox);
// Change: Make sure to initialize the color
color = 1;
carImageView.setColor(color);
carImageView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
// drawingView = new DrawingView(carImageView.getContext(),null);
carImageView.setColor(color);
return false;
}
});
scratchesCb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if (b) {
color = 1;
carImageView.clearCanvas();
carImageView.setColor(1); //
chipsCb.setChecked(false);
dentsCb.setChecked(false);
}
}
});
chipsCb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if (b) {
color = 2;
carImageView.setColor(2);
scratchesCb.setChecked(false);
dentsCb.setChecked(false);
}
}
});
dentsCb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if (b) {
color = 3;
// Change: Do like the other check boxes althogh not really needed.
carImageView.setColor(3);
chipsCb.setChecked(false);
scratchesCb.setChecked(false);
}
}
});
}
}
DrawingView.java (Updated)
public class DrawingView extends android.support.v7.widget.AppCompatImageView {
private Path mPath;
private Paint mPaint;
private float mX, mY;
private static final float TOLERANCE = 5;
int color;
Context context;
public DrawingView(Context context) {
super(context);
this.context = context;
init();
}
public DrawingView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
this.context = context;
init();
}
public void init() {
mPath = new Path();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.MITER);
mPaint.setStrokeWidth(5f);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(mPath, mPaint);
}
public void setColor(int color) {
if (color == 1) {
mPaint.setColor(Color.RED);
this.color = color;
invalidate();
} else if (color == 2) {
mPaint.setColor(Color.BLUE);
this.color = color;
invalidate();
} else if (color == 3) {
mPaint.setColor(Color.GREEN);
this.color = color;
invalidate();
}
}
public int getColor() {
return this.color;
}
private void onStartTouch(float x, float y) {
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void moveTouch(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOLERANCE || dy >= TOLERANCE) {
mPath.quadTo(mX, mY, (mX + x) / 2, (mY + y) / 2);
mX = x;
mY = y;
}
}
public void clearCanvas() {
mPath.reset();
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
invalidate();
}
private void upTouch() {
mPath.lineTo(mX, mY);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
onStartTouch(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
moveTouch(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
upTouch();
invalidate();
break;
}
return true;
}
}
activity_main.xml (Updated)
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:orientation="horizontal"
android:weightSum="3">
<HorizontalScrollView
android:id="#+id/horizontalSrollView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_weight="1">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:paddingTop="5dp"
android:weightSum="2">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginRight="2dp"
android:layout_weight="0.5"
android:background="#android:color/holo_red_light" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="2dp"
android:layout_weight="1.5"
android:text="Scratches"
android:textColor="#000" />
</LinearLayout>
<CheckBox
android:id="#+id/scratchesCheckBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:checked="true" />
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:paddingTop="5dp"
android:weightSum="2">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginRight="2dp"
android:layout_weight="0.5"
android:background="#android:color/holo_blue_light" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="2dp"
android:layout_weight="1.5"
android:text="Chips"
android:textColor="#000" />
</LinearLayout>
<CheckBox
android:id="#+id/ChipCheckbx"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:paddingTop="5dp"
android:weightSum="2">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginRight="2dp"
android:layout_weight="0.5"
android:background="#android:color/holo_green_light" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="2dp"
android:layout_weight="1.5"
android:text="Dings/Dents"
android:textColor="#000" />
</LinearLayout>
<CheckBox
android:id="#+id/DentsCheckBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</LinearLayout>
</RelativeLayout>
</LinearLayout>
</HorizontalScrollView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="8.7">
<[your package name].DrawingView
android:id="#+id/carImageView"
android:layout_width="200dp"
android:layout_height="200dp"
android:src="#mipmap/ic_launcher"
android:layout_gravity="center_vertical" />
<!--<ImageView
android:id="#+id/carImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"/>-->
</LinearLayout>
</LinearLayout>
you need change function setColor.
1.change mPaint color.
2.add invalidate() for redraw view.
public void setColor(int color){
this.color=color;
mPaint.setColor(color);
invalidate();
}
You need to call invalidate() on the View to make it update.
Try this,
final DrawingView drawingView = new DrawingView(carImageView.getContext());
carImageView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
drawingView.setColor(color);
return false;
}
});
scratchesCb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if(b)
{
color = 1;
drawingView.setColor(color);
chipsCb.setChecked(false);
dentsCb.setChecked(false);
}
}
});
chipsCb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if(b)
{
color = 2;
drawingView.setColor(color);
scratchesCb.setChecked(false);
dentsCb.setChecked(false);
}
}
});
dentsCb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if(b)
{
color = 3;
drawingView.setColor(color);
chipsCb.setChecked(false);
scratchesCb.setChecked(false);
}
}
});
DrawingView.java
public DrawingView(Context context) {
super(context);
this.context = context;
init();
}
public DrawingView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
this.context=context;
init();
}
private void init() {
mPath=new Path();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.MITER);
mPaint.setStrokeWidth(5f);
setColor(color);
}
public void setColor(int color){
this.color=color;
if(color==1)
mPaint.setColor(Color.RED);
else if(color==2)
mPaint.setColor(Color.BLUE);
else if(color==3)
mPaint.setColor(Color.GREEN);
// Call invalidate
invalidate();
}
Define static data member in your DrawingView
static int color = 1; //default
Then, from your activity simply call
DrawingView.color = someValue;
Static Keyword before variable color will make sure there is only one variable reference for all objects of your DrawingView class.
"even if I pass a static value, say 3 in main activity in drawingview.setColor(int) it gives black. This means the setColor function in Drawingview isn't working. "
Does this mean it will call paint.setColor(3) ?
If yes, this will of course turn your color black. Try passing Color.GREEN instead

Intersecting two images in different Layouts

How it looks
I want to check intersection between ImageView to ImageView2 as LinearLayout slides towards ImageView2.
I used Rect but it is not working, ImageView2 Just passed throught it without getting intersect.
Please help me!!
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true" >
<ImageView
android:id="#+id/tile"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="#drawable/tile" />
<LinearLayout
android:id="#+id/l1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="100" >
<ImageView
android:id="#+id/b1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="50"
android:background="#000"
android:src="#drawable/b" />
<ImageView
android:id="#+id/b2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="70dp"
android:layout_weight="50"
android:background="#000"
android:src="#drawable/b" />
</LinearLayout>
#SuppressLint("NewApi")
public class MainActivity extends Activity {
Rect tileRect = new Rect();
Rect b1Rect = new Rect();
Rect b2Rect = new Rect();
ImageView tile,b1,b2;
RelativeLayout layout;
LinearLayout l1;
final Handler h = new Handler();
Boolean tileRight=false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
move();
}
private void init() {
// TODO Auto-generated method stub
b1 = (ImageView)findViewById(R.id.b1);
b2 = (ImageView)findViewById(R.id.b2);
tile = (ImageView)findViewById(R.id.tile);
layout = (RelativeLayout)findViewById(R.id.layout);
l1 = (LinearLayout)findViewById(R.id.l1);
tile.setX(320);
tile.setY(800);
l1.setVisibility(View.VISIBLE);
}
public void move()
{
final int delay = 45;
h.postDelayed(new Runnable()
{
#Override
public void run() {
// TODO Auto-generated method stub
layout.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View arg0, MotionEvent event) {
// TODO Auto-generated method stub
if(event.getAction() == MotionEvent.ACTION_UP)
{
if(tileRight==true)
tileRight=false;
else
tileRight=true;
return true;
}
return false;
}
});
if(tileRight==true)
{
if(tile.getX()>600f)
{
tile.setX(tile.getX());
}
else{
tile.setX(tile.getX()+speedTile);
}
}
else{
if(tile.getX()<40f)
{
tile.setX(tile.getX());
}
else{
tile.setX(tile.getX()-speedTile);
}
}
tile.getHitRect(tileRect);
b1.getHitRect(b1Rect);
b2.getHitRect(b2Rect);
if(Rect.intersects(tileRect, b1Rect) || Rect.intersects(tileRect, b2Rect))
{
gameOver();
}
l1.setY(l1.getY()+10f);
h.postDelayed(this, delay);
}
},delay);
}
private void gameOver() {
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
Android's View has getLocationOnScreen() method that shows absolute position of your view.
https://developer.android.com/reference/android/view/View.html#getLocationOnScreen(int[])
The issue occurs because view.getHitRect() is used before the layouts are inflated.
Any of the view's position or measurement APIs like getWidth(), getTop(), getRight() etc. will return 0 (getHitRect() initializes the Rect with (0,0,0,0)) in onCreate() or onResume() before the views are inflated.
In your case, it appears that the Handler's delay period executes the intersection logic earlier than view inflation.
You could post from the view and the run() method will execute after the view inflates and then obtain the view's measurement parameters or a Rect .
Here is an example:
public class MyTestActivity extends AppCompatActivity{
int mNumberOfViewsInitialized = 0;
ImageView mImageViewRight, mImageViewLeft;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sliding_image_activity);
mImageViewLeft = (ImageView) findViewById(R.id.image_view_left);
mImageViewRight = (ImageView) findViewById(R.id.image_view_right);
// calling method here will log that the views do not intersect
// which happens because they haven't been inflated yet.
// doViewsOverlap();
mImageViewLeft.post(new Runnable(){
#Override
public void run() {
mNumberOfViewsInitialized++;
intersectionLogic();
}
});
mImageViewRight.post(new Runnable(){
#Override
public void run() {
mNumberOfViewsInitialized++;
intersectionLogic();
}
});
}
private void intersectionLogic(){
/* the constant could be something else, depending
on the number of views you'd like to test intersection for*/
if(mNumberOfViewsInitialized == 2){
doViewsOverlap(mImageViewLeft,mImageViewRight);
}
}
private void doViewsOverlap(final View firstView, final View secondView){
Rect leftRect = new Rect();
firstView.getHitRect(leftRect);
Rect rightRect = new Rect();
secondView.getHitRect(rightRect);
if(Rect.intersects(leftRect,rightRect) || Rect.intersects(rightRect,leftRect))
Log.v("intersects","views intersect");
else
Log.v("intersects","views do not intersect");
}
}
I used a layout which does not animate / move the images, but the same issue should occur when the images are moved as well
<ImageView
android:id="#+id/image_view_left"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_alignParentLeft="true"
android:background="#android:color/holo_blue_dark"
android:src = "#mipmap/ic_launcher"/>
<ImageView
android:id="#+id/image_view_right"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_alignParentRight="true"
android:background="#android:color/holo_green_dark"
android:gravity="end"
android:src="#mipmap/ic_launcher"/>
Images contained in a RelativeLayout.
Here is a screenshot of the UI:
This problem is related to this: If I call getMeasuredWidth() or getWidth() for layout in onResume they return 0
Hope this helps.

Hide linearlayout on webview scroll

I have an app screen, where I have a webview, and a linear layout of buttons at the bottom of the screen. I have a scroll listener attached to the webview, which is giving me values when scrolled. What I really want to do, is gradually hide the bottom linear layout when scrolling down, and show when scrolling up, as seen in a fair few apps, but all the examples I can see are toolbars at the top of the screen.
I have tried a number of things, I have tried getting the height of the linear layout, and then on scrolling, move it in the y axis to hide and show, and this moved the buttons within but not the actual bar.
The main thing I tried, is when scrolling, decreasing the height of the bar and when it reaches 0 hiding it, and then showing it again, but this is squashing the buttons rather than scrolling them off the bottom of the screen without changing their size.
This is the layout:
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:id="#+id/buttonBar"
android:background="#403152"
android:visibility="gone"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp">
<ImageButton
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="#+id/facebookLink"
android:src="#string/button1"
android:layout_weight="1"
android:onClick="openLink"
android:background="#android:color/transparent"/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="#+id/linkedinLink"
android:src="#string/button2"
android:layout_weight="1"
android:background="?android:selectableItemBackground"
android:onClick="openLink"/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="#+id/twitterLink"
android:src="#string/button3"
android:layout_weight="1"
android:background="?android:selectableItemBackground"
android:onClick="openLink"/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="#+id/instagramLink"
android:src="#string/button4"
android:layout_weight="1"
android:background="?android:selectableItemBackground"
android:onClick="openLink"/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:src="#mipmap/ic_overflow"
android:layout_weight="1"
android:layout_gravity="center"
android:background="?android:selectableItemBackground"
android:onClick="showPopup"
android:id="#+id/aboutoption"/>
</LinearLayout>
<holidays.ObservableWebView
android:id="#+id/scrollableWebview"
android:layout_width="fill_parent"
android:clickable="false"
android:layout_height="fill_parent"
android:layout_alignParentTop="true"
android:layout_above="#id/buttonBar"
android:visibility="gone"/>
and then this is the code I have tried so far, which includes changing the height, etc - Hoping someone may be able to point me in the right direction of what I am doing wrong, or what may be a better way to tackle this.
mWebView.setOnScrollChangedCallback(new ObservableWebView.OnScrollChangedCallback(){
public void onScroll(int l, int t){
int height = mWebView.getMeasuredHeight();
Log.d("HEIGHT", Integer.toString(height));
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int screenHeight = size.y;
Log.d("Screen HEIGHT", Integer.toString(screenHeight));
LinearLayout layout = (LinearLayout)findViewById(R.id.buttonBar);
View contentsView = findViewById(R.id.buttonBar);
if(firstScroll == true){
mWebView.setLayoutParams(new RelativeLayout.LayoutParams(mWebView.getMeasuredWidth(), mWebView.getMeasuredHeight() + 10));
/*originalButtonBarHeight = findViewById(R.id.buttonBar).getHeight();
currentButtonBarHeight = originalButtonBarHeight;
contentsView.getLocationOnScreen(screenBarPosition);
contentsView.setY(screenBarPosition[1] + 1);
firstScroll = false;
layout.getLayoutParams().height = t + 1;
layout.requestLayout();
directionVal = t;*/
}
else if(firstScroll == false){
if(directionVal < t){
mWebView.setLayoutParams(new RelativeLayout.LayoutParams(mWebView.getMeasuredWidth(), mWebView.getMeasuredHeight() + 10));
/* Log.d("DOWN", Integer.toString(t));
if(currentButtonBarHeight <= 0){
//do nothing
}
else{
currentButtonBarHeight = currentButtonBarHeight - 5;
if(currentButtonBarHeight <= 0){
currentButtonBarHeight = 0;
layout.getLayoutParams().height = 0;
layout.requestLayout();
findViewById(R.id.buttonBar).setVisibility(View.GONE);
}
else{
contentsView.getLocationOnScreen(screenBarPosition);
contentsView.setY(screenBarPosition[1] + 1);
layout.getLayoutParams().height = currentButtonBarHeight;
layout.requestLayout();
}
}
/*if(t + 5 < originalButtonBarHeight) {
}*/
}
else if(directionVal > t){
Log.d("UP", Integer.toString(currentButtonBarHeight));
/*decreasedAmount = t - 5;
if (decreasedAmount < originalButtonBarHeight){
layout.getLayoutParams().height = t - 5;
layout.requestLayout();
}*/
}
directionVal = t;
}
}
});
You can do this one by CustomWebView:
CustomWebView.java:
public class CustomWebView extends WebView {
private GestureDetector gestureDetector;
public CustomWebView(Context context) {
super(context);
}
public CustomWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomWebView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
return gestureDetector.onTouchEvent(ev) || super.onTouchEvent(ev);
}
public void setGestureDetector(GestureDetector gestureDetector) {
this.gestureDetector = gestureDetector;
}
}
web_fragment.xml:
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/transparent"
android:orientation="vertical">
<com.customview.CustomWebView
android:id="#+id/customWebView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true" />
// Your other Linear layout
</LinearLayout>
CustomeGestureDetector clss for Gesture Detection (I have added in Fragment):
private class CustomeGestureDetector extends GestureDetector.SimpleOnGestureListener {
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if(e1 == null || e2 == null) return false;
if(e1.getPointerCount() > 1 || e2.getPointerCount() > 1) return false;
else {
try {
if(e1.getY() - e2.getY() > 20 ) {
// Show Your Linear Layout
customWebView.invalidate();
return false;
}
else if (e2.getY() - e1.getY() > 20 ) {
// Hide Your Linear Layout
customWebView.invalidate();
return false;
}
} catch (Exception e) {
customWebView.invalidate();
}
return false;
}
}
}
WebFragment.java:
private CustomWebView customWebView;
customWebView= (CustomWebView) view.findViewById(R.id.customWebView);
customWebView.setGestureDetector(new GestureDetector(new CustomeGestureDetector()));
Hope it would help you.
Make your root view as CoordinatorLayout.
Wrap your content into NestedScrollView.
Add following to your bottom View:
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior">

How to make slide to unlock button in android

Hi I want a button that should work as 'slide to unlock' button of IOS
in short I want a button that has no click effect but can slide left to right while drag and on drag completion it should considered click.
please suggest me any sample code if possible.
Thanks!
First of all I'd like to thank #matthias for his answer.
I have used the following seek bar with some customization:
<SeekBar
android:id="#+id/myseek"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:clickable="false"
android:max="100"
android:progressDrawable="#android:color/transparent"
android:thumb="#drawable/ic_launcher" />
and in java code
sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (seekBar.getProgress() > 95) {
} else {
seekBar.setThumb(getResources().getDrawable(R.drawable.ic_launcher));
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
if(progress>95){
seekBar.setThumb(getResources().getDrawable(R.drawable.load_img1));
}
}
});
I started out with the example that Jignesh Ansodariya posted, but as Aerrow points out, the user can click anywhere on the SeekBar to unlock. That makes it quite unusable, since the point with having a slide button is that accidental clicks should be ignored. My solution was to create a subclass of SeekBar, like this:
public class SlideButton extends SeekBar {
private Drawable thumb;
private SlideButtonListener listener;
public SlideButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void setThumb(Drawable thumb) {
super.setThumb(thumb);
this.thumb = thumb;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (thumb.getBounds().contains((int) event.getX(), (int) event.getY())) {
super.onTouchEvent(event);
} else
return false;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
if (getProgress() > 70)
handleSlide();
setProgress(0);
} else
super.onTouchEvent(event);
return true;
}
private void handleSlide() {
listener.handleSlide();
}
public void setSlideButtonListener(SlideButtonListener listener) {
this.listener = listener;
}
}
public interface SlideButtonListener {
public void handleSlide();
}
XML:
<package.SlideButton
android:id="#+id/unlockButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="false"
android:max="100"
android:progressDrawable="#android:color/transparent"
android:thumb="#drawable/button_lock" >
</package.SlideButton>
And finally the code inside my Activity:
((SlideButton) findViewById(R.id.unlockButton)).setSlideButtonListener(new SlideButtonListener() {
#Override
public void handleSlide() {
unlockScreen();
}
});
There are some good libraries to do the trick for you.
If using a library to perform this is not an issue for you, then consider trying this one:
https://github.com/cortinico/slidetoact
Happy coding..!! :)
Android provides the Switch widget that is similar to slide to unlock. However, you will have to customize it a little, e.g. disable change on click.
You can use this library to quickly and easy customize your unlock.
https://github.com/cheekiat/SlideToUnlock
Use this code on xml
<cheekiat.slideview.SlideView
android:id="#+id/slide_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:slideBackground="#drawable/orangesquarebutton"
app:slideSrc="#drawable/slide_image"
app:slideText="Slide to unlock"
app:slideTextColor="#ffffff"
app:slideTextSize="10dp" />
Slide to unlock screenshort
For some reason, I couldn't disable the touch action so accidental taps are still possible
I came up with this solution, it basically won't allow changing the progress by more than 10 values per change event
int unlockLastSeekVal = 0;
// there are skips btwn changes, use value greater than 1
// 10 is great for seekbars with 100 values
int unlockSeekSensitivity = 10;
// final stage to "unlock"; 0.9 => 90%
Double unlockFinalStage = 0.9;
//...........
unlock.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser){
if (Math.abs(unlockLastSeekVal - progress) > unlockSeekSensitivity){
// too much delta, revert to last value
seekBar.setProgress(unlockLastSeekVal);
}else{
unlockLastSeekVal = progress;
}
}
}
public void onStartTrackingTouch(SeekBar seekBar) {
}
public void onStopTrackingTouch(SeekBar seekBar) {
if (seekBar.getProgress() > seekBar.getMax() * unlockFinalStage){
DoYourThing();
}
unlockLastSeekVal = 0;
seekBar.setProgress(0);
}
});
Starting from Oskar's answer (thanks for your contribute) i create a simple example project to manage Slide Button (horizontal and vertical) :
https://github.com/rcaboni/AndroidSlideButton
For a screen shot : https://raw.githubusercontent.com/rcaboni/AndroidSlideButton/master/screenshot.jpg
This is the main method :
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
if (orientation == ORIENTATION_HORIZONTAL) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
int x= (int) event.getX();
int y= (int) event.getY();
if (thumb.getBounds().contains((int) event.getX(), (int) event.getY())) {
super.onTouchEvent(event);
} else
return false;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
if (getProgress() > 70)
handleSlide();
setProgress(0);
} else
super.onTouchEvent(event);
}else{
int i=0;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (event.getAction() == MotionEvent.ACTION_DOWN) {
int x= (int) event.getX();
int y= (int) event.getY();
if (!thumb.getBounds().contains((int) event.getY(), (int) event.getX())) {
return false;
}
}
case MotionEvent.ACTION_MOVE:
i=getMax() - (int) (getMax() * event.getY() / getHeight());
setProgress(100 - i);
onSizeChanged(getWidth(), getHeight(), 0, 0);
break;
case MotionEvent.ACTION_UP:
i=getMax() - (int) (getMax() * event.getY() / getHeight());
if (i < 30) {
handleSlide();
}
setProgress(0);
onSizeChanged(getWidth(), getHeight(), 0, 0);
break;
case MotionEvent.ACTION_CANCEL:
break;
}
}
return true;
}
XML for vertical button :
<RelativeLayout
android:layout_width="75dp"
android:layout_height="130dp"
android:background="#drawable/slide_background_green"
android:id="#+id/lSlideButtonV"
android:layout_below="#+id/lSlideButton"
android:layout_marginTop="50dp">
<TextView
android:layout_width="20dp"
android:layout_height="match_parent"
android:text="SOS"
android:id="#+id/tvSlideActionV"
android:gravity="center|bottom"
android:layout_alignParentRight="false"
android:layout_alignParentEnd="false"
android:layout_alignParentLeft="false"
android:layout_alignParentStart="false"
android:textSize="20dp"
android:textColor="#android:color/white"
android:layout_alignParentTop="false"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="false"
android:layout_marginBottom="15dp" />
<it.aldea.android.widget.SlideButton
android:id="#+id/unlockButtonV"
android:layout_width="match_parent"
android:layout_height="150dp"
android:clickable="false"
android:max="100"
slideButton:orientation="vertical"
android:progressDrawable="#android:color/transparent"
android:thumb="#drawable/slide_track_red"
android:indeterminate="false"
android:layout_marginRight="5dp"
android:layout_marginTop="20dp"
android:layout_centerInParent="true"
android:layout_marginBottom="10dp"
android:thumbOffset="-2dp">
</it.aldea.android.widget.SlideButton>
</RelativeLayout>
It's not a real complete widget because is composed from two view (TextView and SlideButton) into a Layout, but it's a easy configurable solution for Slide Button with text inside.
I hope this is useful for someone.
It maybe very late but I have created a small library for this very purpose. It allows you to swipe and customise the behaviour of your button from xml. Slide Button
Basically it involves overriding of the onTouch() event and making changes according to the coordinates that are received. Its a simple thing after that to set the background as you want and customise the text.
You can rebuild the normal SeekBar to do what you want:
With:
Starting point (20)
Crossing line (90)
And auto reset.
seekBar.setOnSeekBarChangeListener(
new SeekBar.OnSeekBarChangeListener() {
Integer point = 0;
Integer startPoint = 0;
boolean started = true;
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean wasUserInput) {
point = i;
if (started && i > 0 && wasUserInput) {
startPoint = new Integer(i);
started = false;
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (point > 90 && startPoint < 20) { // slided to the right correctly.
// TODO::
} else { // reset.
resetSeekBar(seekBar, point);
}
startPoint = 0;
started = true;
}
});
And:
/**
* Resetting the seekbar, on point at a time.
* #param seekBar Reference to the seekbar made smaller.
* #param oldPoint The point where the dot is atm.
*/
private void resetSeekBar(final SeekBar seekBar, final int oldPoint) {
if (oldPoint > 0) {
final int newPoint = oldPoint -1;
seekBar.setProgress(newPoint);
timer.schedule(new TimerTask() {
final SeekBar seekBar = seekBarBid;
#Override
public void run() {
resetSeekBar(seekBar, newPoint);
}
}, 3);
} else {
seekBar.setProgress(oldPoint);
}
}
I Hope below Ans is work,
public class UnlockSliderView extends FrameLayout {
#BindView(R2.id.tv_unlock_slider)
TextView tvUnlockSlider;
#BindView(R2.id.iv_circle_slide)
ImageView ivCircleSlide;
private View parentCircle;
private float xOrigin = 0;
private float xOriginCircle = 0;
private boolean circleTouched = false;
public UnlockSliderView(Context context) {
super(context);
init();
}
public UnlockSliderView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public UnlockSliderView(Context context, AttributeSet attr, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
inflate(getContext(), R.layout.layout_unlock_slider_view, this);
ButterKnife.bind(this);
parentCircle = (View) ivCircleSlide.getParent();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
eventActionDown(event);
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
eventActionMove(event);
} else if (event.getAction() == MotionEvent.ACTION_UP) {
unlockFinish();
}
return true;
}
private void eventActionDown(MotionEvent event) {
if ((event.getX() >= ivCircleSlide.getX() && event.getX() <= ivCircleSlide.getX() + ivCircleSlide.getWidth())
&& (event.getY() >= ivCircleSlide.getY() && event.getY() <= ivCircleSlide.getY() + ivCircleSlide.getHeight())) {
xOrigin = event.getX();
xOriginCircle = ivCircleSlide.getX();
circleTouched = true;
} else {
circleTouched = false;
}
}
private void eventActionMove(MotionEvent event) {
if (circleTouched) {
float newXCircle = xOriginCircle + (event.getX() - xOrigin);
newXCircle = (newXCircle < xOriginCircle) ? xOriginCircle : newXCircle;
newXCircle = (newXCircle > parentCircle.getWidth() - ivCircleSlide.getWidth() - xOriginCircle) ? parentCircle.getWidth() - ivCircleSlide.getWidth() - xOriginCircle : newXCircle;
float alpha = 1 - ((newXCircle - xOriginCircle) / (parentCircle.getWidth() - ivCircleSlide.getWidth() - (xOriginCircle * 2)));
tvUnlockSlider.setAlpha(alpha);
ivCircleSlide.setX(newXCircle);
if (newXCircle == parentCircle.getWidth() - ivCircleSlide.getWidth() - xOriginCircle) {
unlockFinish();
if (mListener != null) mListener.onUnlock();
}
}
}
private void unlockFinish() {
if (circleTouched) {
ivCircleSlide.animate().x(xOriginCircle).setDuration(400).start();
tvUnlockSlider.animate().alpha(1).setDuration(400).start();
circleTouched = false;
}
}
and the xml is,
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="#dimen/unlock_slider_height"
android:layout_margin="#dimen/default_padding"
android:background="#drawable/btn_slider_back"
android:gravity="center"
android:orientation="vertical"
android:padding="4dp">
<TextView
android:id="#+id/tv_unlock_slider"
style="#style/prelogin_slider"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_marginLeft="#dimen/unlock_slider_handle_size"
android:layout_marginStart="#dimen/unlock_slider_handle_size"
android:text="#string/logon_to_mobile_banking" />
<ImageView
android:id="#+id/iv_circle_slide"
android:layout_width="#dimen/unlock_slider_handle_size"
android:layout_height="#dimen/unlock_slider_handle_size"
android:scaleType="fitCenter"
android:src="#drawable/btn_slider_handle"
tools:ignore="ContentDescription" />
</FrameLayout>
Thanks, #Oskar Lundgren for the answer I have updated some of the things and if anybody is looking to do the same in kotlin. Here it is
SlideToConfirm
class SlideToConfirm : SeekBar, SeekBar.OnSeekBarChangeListener {
private lateinit var listener: SlideButtonListener
// To prevent the thumb to going out of track
private val maxProgress = 91
private val minProgress = 9
constructor(context: Context) : super(context) {
init()
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init()
}
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
init()
}
fun init() {
setDrawables()
setProperties()
setOnSeekBarChangeListener(this)
}
private fun setDrawables() {
thumb = ContextCompat.getDrawable(context, R.drawable.slider_thumb)
progressDrawable = ContextCompat.getDrawable(context, R.drawable.slider_progress_drawable)
}
private fun setProperties() {
isClickable = false
splitTrack = false
setPadding(0, 0, 0, 0)
progress = minProgress
}
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
if (progress < minProgress) {
this.progress = minProgress
}
if (progress > maxProgress) {
this.progress = maxProgress
}
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
override fun onStopTrackingTouch(seekBar: SeekBar?) {}
#SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent?): Boolean {
if (event!!.action == MotionEvent.ACTION_DOWN) {
if (thumb.bounds.contains(event.x.toInt(), event.y.toInt())) {
super.onTouchEvent(event)
} else
return false
} else if (event.action == MotionEvent.ACTION_UP) {
if (progress > 70) {
handleSlide()
progress = maxProgress
} else {
progress = minProgress
}
} else {
super.onTouchEvent(event)
}
return true
}
fun setOnSlideListener(listener: SlideButtonListener) {
this.listener = listener
}
private fun handleSlide() {
listener.handleSlide()
}
interface SlideButtonListener {
fun handleSlide()
}
}
Thumb
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="oval">
<solid android:color="#color/cyan" />
<corners android:radius="8dp" />
<size
android:width="50dp"
android:height="50dp" />
</shape>
</item>
<item android:drawable="#drawable/ic_arrow_forward_white" />
</layer-list>
Progress Track
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="35dp" />
<size
android:width="100dp"
android:height="50dp" />
<stroke
android:width="1dp"
android:color="#color/errorRed" />
<solid android:color="#android:color/white" />
</shape>

Categories

Resources