Drawing pattern by accelerometer [closed] - java

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I have implemented pin entry method using accelerometer to unlock android smartphones in Java, now I want to update it by drawing pattern using acclerometer. can anyone help me because I dont have any idea about it.
thanks in advance
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/main_view"
android:background="#ff00ff00">
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="21dp"
android:layout_marginTop="28dp"
android:text=""
android:background="#drawable/red_button"/>
<Button
android:id="#+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/button1"
android:layout_centerVertical="true"
android:text=""
android:background="#drawable/blue_button" />
<Button
android:id="#+id/button6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/button4"
android:layout_below="#+id/button4"
android:layout_marginLeft="26dp"
android:layout_marginTop="88dp"
android:text=""
android:background="#drawable/rep_button"/>
<Button
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="#+id/button5"
android:layout_centerHorizontal="true"
android:layout_marginBottom="46dp"
android:background="#drawable/yellow_button" />
<Button
android:id="#+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/button1"
android:layout_alignBottom="#+id/button1"
android:layout_toRightOf="#+id/button2"
android:background="#drawable/black_button" />
<Button
android:id="#+id/button5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="#+id/button6"
android:layout_alignLeft="#+id/button7"
android:background="#drawable/pink_button" />
<Button
android:id="#+id/button7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/button6"
android:layout_alignBottom="#+id/button6"
android:layout_toRightOf="#+id/button2"
android:background="#drawable/org_button" />
</RelativeLayout>

Here is some open source code I found that can get you started:
Here is the MainActivity:
import android.graphics.Point;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.os.Handler;
import android.view.Display;
import android.view.Window;
import android.view.WindowManager.LayoutParams;
import android.widget.FrameLayout;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorManager;
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.SensorEventListener;
import android.widget.Toast;
public class TiltBall extends ActionBarActivity {
BallView mBallView = null;
Handler RedrawHandler = new Handler(); //so redraw occurs in main thread
Timer mTmr = null;
TimerTask mTsk = null;
int mScrWidth, mScrHeight;
android.graphics.PointF mBallPos, mBallSpd;
#Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE); //hide title bar
//set app to full screen and keep screen on
getWindow().setFlags(0xFFFFFFFF, LayoutParams.FLAG_FULLSCREEN | LayoutParams.FLAG_KEEP_SCREEN_ON);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tilt_ball);
//create pointer to main screen
final FrameLayout mainView = (android.widget.FrameLayout) findViewById(R.id.main_view);
//get screen dimensions
Display display = getWindowManager().getDefaultDisplay();
mScrWidth = display.getWidth();
mScrHeight = display.getHeight();
mBallPos = new android.graphics.PointF();
mBallSpd = new android.graphics.PointF();
//create variables for ball position and speed
mBallPos.x = mScrWidth / 2;
mBallPos.y = mScrHeight / 5;
mBallSpd.x = 0;
mBallSpd.y = 0;
//create initial ball
mBallView = new BallView(this, mBallPos.x, mBallPos.y, 75);
mainView.addView(mBallView); //add ball to main screen
mBallView.invalidate(); //call onDraw in BallView
//listener for accelerometer, use anonymous class for simplicity
((SensorManager) getSystemService(Context.SENSOR_SERVICE)).registerListener(
new SensorEventListener() {
#Override
public void onSensorChanged(SensorEvent event) {
//set ball speed based on phone tilt (ignore Z axis)
mBallSpd.x = -event.values[0] * (30/10);
mBallSpd.y = event.values[1] * (30/10);
//timer event will redraw ball
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
} //ignore
},
((SensorManager) getSystemService(Context.SENSOR_SERVICE))
.getSensorList(Sensor.TYPE_ACCELEROMETER).get(0),
SensorManager.SENSOR_DELAY_NORMAL);
//listener for touch event
mainView.setOnTouchListener(new android.view.View.OnTouchListener() {
public boolean onTouch(android.view.View v, android.view.MotionEvent e) {
//set ball position based on screen touch
mBallPos.x = e.getX();
mBallPos.y = e.getY();
//timer event will redraw ball
return true;
}
});
} //OnCreate
//listener for menu button on phone
#Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add("Exit"); //only one menu item
return super.onCreateOptionsMenu(menu);
}
//listener for menu item clicked
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
if (item.getTitle() == "Exit") //user clicked Exit
finish(); //will call onPause
return super.onOptionsItemSelected(item);
}
//For state flow see http://developer.android.com/reference/android/app/Activity.html
#Override
public void onPause() //app moved to background, stop background threads
{
mTmr.cancel(); //kill\release timer (our only background thread)
mTmr = null;
mTsk = null;
super.onPause();
}
#Override
public void onResume() //app moved to foreground (also occurs at app startup)
{
//create timer to move ball to new position
mTmr = new Timer();
mTsk = new TimerTask() {
public void run() {
//if debugging with external device,
// a log cat viewer will be needed on the device
Log.d("TiltBall", "Timer Hit - " + mBallPos.x + ":" + mBallPos.y);
//move ball based on current speed
mBallPos.x += mBallSpd.x;
mBallPos.y += mBallSpd.y;
//if ball goes off screen, reposition to opposite side of screen
if (mBallPos.x > mScrWidth) mBallPos.x = 0;
if (mBallPos.y > mScrHeight) mBallPos.y = 0;
if (mBallPos.x < 0) mBallPos.x = mScrWidth;
if (mBallPos.y < 0) mBallPos.y = mScrHeight;
//update ball class instance
mBallView.x = mBallPos.x;
mBallView.y = mBallPos.y;
//redraw ball. Must run in background thread to prevent thread lock.
RedrawHandler.post(new Runnable() {
public void run() {
mBallView.invalidate();
}
});
}
}; // TimerTask
mTmr.schedule(mTsk, 10, 10); //start timer
super.onResume();
} // onResume
#Override
public void onDestroy() //main thread stopped
{
super.onDestroy();
//wait for threads to exit before clearing app
System.runFinalizersOnExit(true);
//remove app from memory
android.os.Process.killProcess(android.os.Process.myPid());
}
//listener for config change.
//This is called when user tilts phone enough to trigger landscape view
// we want our app to stay in portrait view, so bypass event
#Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
}
}
And here is the BallView class:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;
public class BallView extends View {
public float x;
public float y;
private final int r;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//construct new ball object
public BallView(Context context, float x, float y, int r) {
super(context);
//color hex is [transparncy][red][green][blue]
mPaint.setColor(0xFF1325E0); //not transparent. color is blue
this.x = x;
this.y = y;
this.r = r; //radius
}
//qcalled by invalidate()
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(x, y, r, mPaint);
}
public int getRadius(){
return r;
}
}
And you'd use a FrameLayout with this code:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/main_view"
android:background="#ff000000">
</FrameLayout>
This will generate a blue ball that will respond to device tilting with movement accordingly. You can add a poly line mechanism by sampling the balls position and draw connected lines at those connected points accordingly creating a polyline that looks like a continuous smooth line.

Related

Custom circular image progress bar with transparent background

I have circular picture of timer:
I want to create circular progress bar which would consist of this picture, which would make it slowly empty like a clock until full disappearance (like an animation).
for example, after half of the time I will define it's appear as:
Right now I'm doing it in this way:
I put the timer image as image on the screen, and over it I put regular circle progress bar with color as the background color which hides the image (and so I get the desired effect).
but now I have problem when I have few colors in the background of the app, because I can't to make the progress bar with the same color as the background, so I looking for new way to make it, like custom progress bar that consist from the timer image with transparent background.
please help me how to change my code to get it:
ProgressBar XML:
<ProgressBar
android:id="#+id/progressBar"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:progress="100"
android:rotation="90"
style="?android:progressBarStyleHorizontal"
android:progressDrawable="#drawable/prog_blue"/>
Prog_blue (the drawable of the progressBar) - blue is the background color of the app:
<?xml version="1.0" encoding="utf-8"?>
<scale
android:fromDegrees="270"
android:pivotX="50%"
android:pivotY="50%"
xmlns:android="http://schemas.android.com/apk/res/android">
<shape
android:shape="ring"
android:innerRadiusRatio="2.7"
android:thickness="23dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="#color/prog_blue"
android:endColor="#color/prog_blue"
android:type="sweep"
android:useLevel="false"/>
</shape>
</scale>
you can create custom View To achieve that
here My Code :
ClockProgressBar.java
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
public class ClockProgressBar extends View {
Paint tickPaint;
private int shortTickLen=10;
private int longTickLen=20;
private int finalWidth=0;
private int finalHeight=0;
private int r=0;
private Path ticksPath;
int tickNumbers=60;
private ValueAnimator animator;
public ClockProgressBar(Context context) {
super(context);
ini();
}
public ClockProgressBar(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
ini();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawIntervals(canvas);
}
void drawIntervals(Canvas canvas){
double cX = getX()+finalWidth/2;
double cY = getY()+finalHeight/2;
ticksPath=new Path();
for ( int i=1; i<= tickNumbers; i++){
int len = shortTickLen;
if ( i % 15 == 0 ){
len = longTickLen;
}else if ( i % 5 == 0 ){
len = longTickLen;
}
double di = (double)i;
double angleFrom12 = di/60.0*2.0*Math.PI;
double angleFrom3 = Math.PI/2.0-angleFrom12;
ticksPath.moveTo(
(float)(cX+Math.cos(angleFrom3)*r),
(float)(cY-Math.sin(angleFrom3)*r)
);
ticksPath.lineTo(
(float)(cX+Math.cos(angleFrom3)*(r-len)),
(float)(cY-Math.sin(angleFrom3)*(r-len))
);
}
canvas.drawPath(ticksPath,tickPaint);
}
void ini(){
tickPaint=new Paint();
tickPaint.setStrokeCap(Paint.Cap.BUTT);
tickPaint.setStrokeJoin(Paint.Join.ROUND);
tickPaint.setAntiAlias(true);
tickPaint.setStyle(Paint.Style.STROKE);
tickPaint.setStrokeWidth(8);
tickPaint.setStrokeMiter(2f);
tickPaint.setColor(Color.BLACK);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
finalWidth = getMeasuredWidth() / 2;
finalHeight = getMeasuredWidth() / 2;
r = (finalWidth / 2);
setMeasuredDimension(finalWidth, finalHeight );
}
public void startAnimation(){
animator = ValueAnimator.ofInt(60, 0);
animator.setDuration(5000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
tickNumbers= (int) animation.getAnimatedValue();
invalidate();
}
});
animator.setRepeatCount(Integer.MAX_VALUE);
animator.start();
}
public void stopAnimation(){
if (animator!=null){
animator.cancel();
}
setVisibility(GONE);
}
}
and use it in xml
activity_main.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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<<Your Package Name>.ClockProgressBar
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/progress"/>
</LinearLayout>
and call it in you activity and use startAnimation() and stopAnimation() to show and dismiss the View
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ClockProgressBar progressBar=findViewById(R.id.progress);
progressBar.startAnimation();
}
}
final result:
hope it helps

Hiding ‘Bottom Navigation Bar’ whilst keyboard is present - Android

I have a small demo chat UI application. This application has a bottom navigation bar. I need the bottom navigation bar to hide when the keyboard appears.
Here is an example of the chat UI
As you can see when you click in the EditText element, the keyboard appears but the bottom navigation bar stays visible. I have tried methods such as this measurement method, but the UI elements flicker like this.
Is there a proper way to hide the bottom navigation bar when the keyboard is visible?
EDIT:
In the below activity you can see where I set the keyboard listener to adjust the position of UI elements when the keyboard is determined as being visible.
This is my activity code, uses setKeyboardListener method from the above link and set it in onCreateView:
package uk.cal.codename.projectnedry.TeamChatFragment;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Layout;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Toast;
import com.roughike.bottombar.BottomBar;
import java.util.ArrayList;
import butterknife.BindView;
import butterknife.ButterKnife;
import uk.cal.codename.projectnedry.R;
import uk.cal.codename.projectnedry.TeamChatFragment.ListAdapter.TeamChatListAdapter;
import uk.demo.cal.genericmodelviewpresenter.GenericMvp.GenericMvpFragment;
import static android.view.View.GONE;
/**
* A simple {#link Fragment} subclass.
* Activities that contain this fragment must implement the
* {#link TeamChatView.OnFragmentInteractionListener} interface
* to handle interaction events.
* Use the {#link TeamChatView#newInstance} factory method to
* create an instance of this fragment.
*/
public class TeamChatView extends GenericMvpFragment implements TeamChatContract.RequiredViewOps {
private OnFragmentInteractionListener mListener;
#BindView(R.id.teamChatList)
RecyclerView mTeamChatRecyclerView;
#BindView(R.id.teamChatSendButton)
ImageButton mTeamChatSendButton;
#BindView(R.id.messageTextInput)
EditText mMessageTextInput;
TeamChatListAdapter mTeamChatListAdapter;
TeamChatListAdapter.ClickListener mTeamChatListClickListener;
private ArrayList<String> mTestMessageList;
public interface OnKeyboardVisibilityListener {
void onVisibilityChanged(boolean visible);
}
public final void setKeyboardListener(final OnKeyboardVisibilityListener listener) {
final View activityRootView = ((ViewGroup) getActivity().findViewById(android.R.id.content)).getChildAt(0);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
private boolean wasOpened;
private final int DefaultKeyboardDP = 100;
// From #nathanielwolf answer... Lollipop includes button bar in the root. Add height of button bar (48dp) to maxDiff
private final int EstimatedKeyboardDP = DefaultKeyboardDP + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? 48 : 0);
private final Rect r = new Rect();
#Override
public void onGlobalLayout() {
// Convert the dp to pixels.
int estimatedKeyboardHeight = (int) TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_DIP, EstimatedKeyboardDP, activityRootView.getResources().getDisplayMetrics());
// Conclude whether the keyboard is shown or not.
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
boolean isShown = heightDiff >= estimatedKeyboardHeight;
if (isShown == wasOpened) {
Log.d("Keyboard state", "Ignoring global layout change...");
return;
}
wasOpened = isShown;
listener.onVisibilityChanged(isShown);
}
});
}
public TeamChatView() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* #return A new instance of fragment TeamChatView.
*/
public static TeamChatView newInstance() {
TeamChatView fragment = new TeamChatView();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
#SuppressLint("MissingSuperCall")
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(TeamChatPresenter.class, TeamChatModel.class, savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
final View view = inflater.inflate(R.layout.fragment_team_chat_view, container, false);
this.mUnbinder = ButterKnife.bind(this, view);
mTestMessageList = new ArrayList<>();
this.mTeamChatListAdapter = new TeamChatListAdapter(mTestMessageList);
this.mTeamChatRecyclerView.setAdapter(this.mTeamChatListAdapter);
final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
this.mTeamChatRecyclerView.setLayoutManager(linearLayoutManager);
this.mTeamChatSendButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(!String.valueOf(mMessageTextInput.getText()).equals("")) {
getSpecificImpOfGenericPresenter().sendMessage(String.valueOf(mMessageTextInput.getText()));
mMessageTextInput.setText("");
mTeamChatRecyclerView.smoothScrollToPosition(mTestMessageList.size());
}
}
});
setKeyboardListener(new OnKeyboardVisibilityListener(){
#Override
public void onVisibilityChanged(boolean visible) {
RelativeLayout contentFrame = (RelativeLayout) getActivity().findViewById(R.id.content_company_navigation);
BottomBar lowerNavigationBar = (BottomBar) getActivity().findViewById(R.id.bottomBar);
if (visible) { // if more than 100 pixels, its probably a keyboard...
lowerNavigationBar.setVisibility(GONE);
contentFrame.setPadding(0, 0, 0, 0);
mTeamChatRecyclerView.smoothScrollToPosition(mTestMessageList.size());
} else {
contentFrame.setPadding(0, 0, 0, convertDpToPixel(60, getContext()));
mTeamChatRecyclerView.smoothScrollToPosition(mTestMessageList.size());
lowerNavigationBar.setVisibility(View.VISIBLE);
}
}
});
return view;
}
/**
* This method converts dp unit to equivalent pixels, depending on device density.
*
* #param dp A value in dp (density independent pixels) unit. Which we need to convert into pixels
* #param context Context to get resources and device specific display metrics
* #return A float value to represent px equivalent to dp depending on device density
*/
public static int convertDpToPixel(float dp, Context context){
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
int px = (int) (dp * ((float)metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT));
return px;
}
public void addToTestMessageList(String str){
this.mTestMessageList.add(str);
this.mTeamChatListAdapter.notifyDataSetChanged();
}
#Override
public void onDestroyView() {
super.onDestroyView();
// getView().getViewTreeObserver().removeGlobalOnLayoutListener(test);
}
#Override
public TeamChatPresenter getSpecificImpOfGenericPresenter() {
return (TeamChatPresenter) this.mPresenter;
}
}
This is my XML layout:
<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:orientation="vertical"
tools:context="uk.cal.codename.projectnedry.TeamChatFragment.TeamChatView">
<android.support.v7.widget.RecyclerView
android:layout_above="#+id/chatViewMessageEntryLayout"
android:id="#+id/teamChatList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:isScrollContainer="false"
android:paddingTop="10dp"
android:scrollbars="vertical" />
<RelativeLayout
android:id="#+id/chatViewMessageEntryLayout"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_alignParentBottom="true"
android:orientation="horizontal">
<View
android:id="#+id/chatViewMessageEntrySeperator"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#e3e3e8" />
<EditText
android:id="#+id/messageTextInput"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:layout_below="#+id/chatViewMessageEntrySeperator"
android:layout_toLeftOf="#+id/teamChatSendButton"
android:background="#android:color/transparent"
android:hint="Enter message"
android:inputType="textCapSentences|textMultiLine"
android:maxLength="1000"
android:maxLines="4"
android:paddingLeft="10dp" />
<ImageButton
android:id="#+id/teamChatSendButton"
android:layout_width="50dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_gravity="center"
android:background="#00B9EF"
android:src="#drawable/ic_send_white_24dp" />
</RelativeLayout>
</RelativeLayout>
The easiest implementation, Add AndroidManifest.xml in
<activity android:windowSoftInputMode="adjustPan"/>
hopefully this helps someone out. Enjoy !
you just add this code in your manifest like this way..
<activity android:name=".MainActivity"
android:windowSoftInputMode="adjustPan">
this works for me.. happy coding
I ended up using the height measuring method that seems to be the standard way of soft keyboard detection which is described in this answer. However, I used this library's implementation of it, as it is still the same ViewTreeObserver.OnGlobalLayoutListener method, implemented well, and allowed me to abstract the code out of my applications main codebase.
When this keyboard visibility listener is triggered, I then hide/show the bottom navigation bar (which I have explained here).
Add this line in onResume() of your Activity or Fragment.
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
It is worked for me. Just try it once.
Just add the attribute below to every activity in your AndroidManifest.xml file.
<activity
android:name=".MainActivity"
android:windowSoftInputMode="stateAlwaysHidden|adjustPan" />
Actively listen for the Keyboard(IME) open/close events and show/hide bottom navigation accordingly.
We can make use of the WindowInsetsCompat API which makes this job effortless.
Step 1: Make sure you have migrated to AndroidX
Step 2: In your Fragment inside onCreateView add the listener for Keyboard(IME) events
ViewCompat.setOnApplyWindowInsetsListener(window.decorView.rootView) { _, insets ->
//This lambda block will be called, every time keyboard is opened or closed
val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
if(imeVisible){
//Now show-hide bottom navigation accordingly
}
insets
}
This is all you need🎉
For more info on window insets, you can check out this in-depth article
NOTE:Earlier detecting Keyboard open/close events was not an easy task. Android Devs resorted to all types of hacks to accomplish this. But after decades of requests, prayers and rants Google finally came up with WindowInsets API. Thank you, Google🙏🏻
private boolean keyboardListenersAttached = false;
private ViewGroup rootLayout;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_settings, container, false);
rootLayout = (ViewGroup) view.findViewById(R.id.settings_layout);
attachKeyboardListeners();
return view;
}
private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new
ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int heightDiff = rootLayout.getRootView().getHeight() -
rootLayout.getHeight();
if (getActivity() != null) {
int contentViewTop =
getActivity().getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
LocalBroadcastManager broadcastManager =
LocalBroadcastManager.getInstance(getContext());
Rect r = new Rect();
rootLayout.getWindowVisibleDisplayFrame(r);
int screenHeight = rootLayout.getRootView().getHeight();
// r.bottom is the position above soft keypad or device button.
// if keypad is shown, the r.bottom is smaller than that before.
int keypadHeight = screenHeight - r.bottom;
if (keypadHeight > screenHeight * 0.15) {
onHideKeyboard();
Intent intent = new Intent("KeyboardWillHide");
broadcastManager.sendBroadcast(intent);
} else {
int keyboardHeight = heightDiff - contentViewTop;
onShowKeyboard(keyboardHeight);
Intent intent = new Intent("KeyboardWillShow");
intent.putExtra("KeyboardHeight", keyboardHeight);
broadcastManager.sendBroadcast(intent);
}
}else {
}
}
};
protected void onShowKeyboard(int keyboardHeight) {
System.out.println("keyboard is shown");
dashboardActivity.bottomNavigationView.setVisibility(View.VISIBLE);
dashboardActivity.fab.setVisibility(View.VISIBLE);
}
protected void onHideKeyboard() {
System.out.println("keyboard is hide");
dashboardActivity.bottomNavigationView.setVisibility(View.INVISIBLE);
dashboardActivity.fab.setVisibility(View.INVISIBLE);
}
protected void attachKeyboardListeners() {
if (keyboardListenersAttached) {
return;
}
rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(keyboardLayoutListener);
keyboardListenersAttached = true;
}
in Manifest.xml file add
<activity
android:name=".Activity.DashboardActivity"
android:windowSoftInputMode="adjustResize"
/>
For API 21+:
Firstly, set "android:windowSoftInputMode" for the activity to "adjustResize" in AndroidManifest.xml
Secondly, in your acticity's onCreate method register the following listener:
window.decorView.setOnApplyWindowInsetsListener { view, insets ->
val insetsCompat = toWindowInsetsCompat(insets, view)
val isImeVisible = insetsCompat.isVisible(WindowInsetsCompat.Type.ime())
// below line, do the necessary stuff:
dataBinding.bottomNavigation.visibility = if (isImeVisible) View.GONE else View.VISIBLE
view.onApplyWindowInsets(insets)
}
Add the attribute : android:windowSoftInputMode="adjustResize"" in your manifest inside activity tag:
<activity
android:name=".YourActivity"
android:windowSoftInputMode="adjustResize"/>
NB: I suggest, You should use NestedScrollView as the parent layout.
Hope this helps.
In my case, I am using DrawerLayout as a parent view with some layout content and A Navigation Bottom Bar.
In Manifest file add "android:windowSoftInputMode="adjustPan" this TAG with Activity and this working fine.
<activity android:name=".Activities.OrderSceenWithOrder"
android:windowSoftInputMode="adjustPan"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
That answer might be helpful for people still looking for solution.
Bottom Navigation bar moves up with keyboard
Add this onResume() in your fragment
#Override
public void onResume() {
Objects.requireNonNull(getActivity()).getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
super.onResume();
}
Hope this helps someone!
That solution works for me and it also doesn't show overlapped bottom navbar
just add this line to your activity:
android:windowSoftInputMode="adjustResize|adjustPan"

Android App OnClick Crashing

So i have a simple app, just a menu with a few buttons, when a button is clicked you are brought to a new page. The page has a button, which when clicked, keeps changing its background image until it runs out of images (list of image names stored in strings), then you are brought back to the main menu. I can do this twice, then on the third attempt, if i click a button on the menu the app crashes. This doesnt happen on the emulator, only when i run it on my phone. I dont know why this is happening
package com.example.otapp;
import com.example.otapp.R.raw;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.media.MediaPlayer;
public class MainActivity extends ActionBarActivity {
public static String DPExtension;//Holds the letters dp
public String list;
public static int Screen_Height;//holds screen height
public static int Screen_Width;//holds screen width
public Intent intent;
public MediaPlayer audio;
public final static String EXTRA_MESSAGE = "com.example.otapp.MESSAGE";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//this code block is for getting the screen proportions
Display getdisplay = getWindowManager().getDefaultDisplay();
DisplayMetrics dispMetrics = new DisplayMetrics();
getdisplay.getMetrics(dispMetrics);
float densitydp = getResources().getDisplayMetrics().density;
float ScreenHeightdp = dispMetrics.heightPixels / densitydp;
float ScreenWidthdp = dispMetrics.widthPixels / densitydp;
//Below dimension value holders do not use pixel density
float ScreenHeightCheck = dispMetrics.heightPixels;
float ScreenWidthCheck = dispMetrics.heightPixels;
DPExtension = "dp";
Screen_Height = (int) ScreenHeightCheck;
Screen_Width = (int) ScreenWidthCheck;
//The printlns are so I can discern the outputs in LogCat
//System.out.println("Screen Height:" + Screen_Height);
//System.out.println("Screen Width:" + Screen_Width);
View Button1 = findViewById(R.id.Button01);
LinearLayout.LayoutParams params = (LayoutParams) Button1.getLayoutParams();
params.height = Screen_Height/3;
Button1.setLayoutParams(params);
View Button2 = findViewById(R.id.Button02);
Button2.setLayoutParams(params);
View Button3 = findViewById(R.id.Button03);
Button3.setLayoutParams(params);
View Button4 = findViewById(R.id.Button04);
Button4.setLayoutParams(params);
Button1.setOnClickListener(onClickListener);
Button2.setOnClickListener(onClickListener);
Button3.setOnClickListener(onClickListener);
Button4.setOnClickListener(onClickListener);
Button1.setBackgroundResource(getResources().getIdentifier("gettingup", "drawable", getPackageName()));
intent = new Intent(this, DisplayMessageActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
audio = MediaPlayer.create(MainActivity.this, raw.buttonsound);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private OnClickListener onClickListener = new OnClickListener() {
#Override
public void onClick(View v) {
// play sound
audio.start();
// do different things for each different button
switch(v.getId()) {
case R.id.Button01:
list = "Get Up";
intent.putExtra(EXTRA_MESSAGE, list);
startActivity(intent);
break;
case R.id.Button02:
list = "Get Dressed";
intent.putExtra(EXTRA_MESSAGE, list);
startActivity(intent);
break;
case R.id.Button03:
list = "Get Dressed";
intent.putExtra(EXTRA_MESSAGE, list);
startActivity(intent);
break;
case R.id.Button04:
list = "Get Dressed";
intent.putExtra(EXTRA_MESSAGE, list);
startActivity(intent);
break;
}
}
};
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent" android:background="#1E90FF">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:id="#+id/Button01"
android:layout_width="match_parent"
android:layout_height="125dp"
android:text="#string/button_send"/>
<Button
android:id="#+id/Button03"
android:layout_width="match_parent"
android:layout_height="125dp"
android:text="#string/button2_send" />
<Button
android:id="#+id/Button04"
android:layout_width="match_parent"
android:layout_height="125dp"
android:text="#string/button3_send" />
<Button
android:id="#+id/Button02"
android:layout_width="match_parent"
android:layout_height="125dp"
android:text="#string/button4_send" />
</LinearLayout>
</ScrollView>
If the source above is formatted correctly, line 99, which is where the NullPointerException is thrown, is:
audio.start();
This means that audio is null. It is declared on line 83:
audio = MediaPlayer.create(MainActivity.this, raw.buttonsound);
Most likely what's happening is that the MediaPlayer.create is unable to located the raw.buttonsound. I'd debug at this line and verify that the MediaPlayer is failing to create the audio.
Try moving the definition of the onClickListener object into the onCreate method.
You can declare it as a member variable, but you should create the object and assign it to the field in onCreate()

Passing a variable to a Custom View Class from Actvity

I am testing drawing audio frequency into canvas using canvas.drawLine() method. I am able to do static draw into canvas. Basically I have a test app which has two buttons START and STOP and a Canvas where i am trying to draw the audio frequencies obtained from FFT. When i hit the START button it starts recording sound using AudioRecord class and collects data into buffer which i run through FFT to get the frequencies. This is in very early stage. Right now i am only trying to figure out how to pass these frequency values to onDraw method of my Custom View class. Here are the relevent codes starting from the layout.
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: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=".MainActivity" >
<com.example.testapp.WaveForm
android:id="#+id/waveForm1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginTop="78dp" />
<Button
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/button1"
android:layout_alignBottom="#+id/button1"
android:layout_alignRight="#+id/waveForm1"
android:layout_marginRight="19dp"
android:text="STOP" />
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/waveForm1"
android:layout_alignParentTop="true"
android:text="START" />
</RelativeLayout>
I think its very starighforward. Here is my Custom View class
WaveForm.java
package com.example.testapp;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
public class WaveForm extends View {
private Paint mLinePaint;
float x ;
float y;
public WaveForm(Context context, AttributeSet attrs) {
super(context, attrs);
mLinePaint = new Paint();
mLinePaint.setStyle(Paint.Style.STROKE);
mLinePaint.setARGB(255, 0, 0, 255);
mLinePaint.setAntiAlias(true);
mLinePaint.setStrokeWidth(10);
x = 0.0f;
y = 0.0f;
}
#Override
protected void onDraw(Canvas canvas) {
//Instead of 50 here i would like to pass the value obtained from the Activity class
canvas.drawLine(x, 0, y, 50, mLinePaint);
y = y + 1.0f;
x = x + 1.0f;
super.onDraw(canvas);
}
}
As i mentioned in the comment in the onDraw method, i would like to pass the frequency value i obtained in the Activity instead of 50.
Finally here is my Activity:
MainActivity.java
package com.example.testapp;
import android.app.Activity;
import android.graphics.Canvas;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder.AudioSource;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
boolean recording = false;
AudioRecord audioRecord;
double[] x;
double[] y;
double currentPoint = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final View myInstance = (View) findViewById(R.id.waveForm1);
Button start = (Button) findViewById(R.id.button1);
start.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
int sampleRateInHz=44100;
int channelConfig = AudioFormat.CHANNEL_IN_MONO;
int bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, AudioFormat.ENCODING_PCM_16BIT);
audioRecord = new AudioRecord(AudioSource.MIC, sampleRateInHz, channelConfig, AudioFormat.ENCODING_PCM_16BIT, AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, AudioFormat.ENCODING_PCM_16BIT));
byte[] audioData = new byte[bufferSizeInBytes/4];
FFT myFFT = new FFT(audioData.length);
Canvas canvas = new Canvas();
recording = true;
audioRecord.startRecording();
while(recording) {
audioRecord.read(audioData,0, bufferSizeInBytes/4);
//convert to double here
for(int i=0; i<audioData.length; i++) {
x[i] = (double)audioData[i];
y[i] = 0;
}
double[] samples = myFFT.fft(x, y);
for(int i=0; i<samples.length; i++) {
currentPoint = samples[i];
myInstance.draw(canvas);
myInstance.invalidate();
}
}
}
});
Button stop = (Button) findViewById(R.id.button2);
stop.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
recording = false;
audioRecord.stop();
}
});
}
}
The currentPoint variable is what i am trying to pass to the onDraw method. Thanks in advance for taking a look.
Create Getter/Setter for currentPoint variable inside WaveForm to get value from MainActivity before calling draw as:
public class WaveForm extends View {
private double currentPoint = 50;
public void setCurrentPoint(double currentPoint){
this.currentPoint=currentPoint;
}
public double getCurrentPoint(){
return this.currentPoint;
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawLine(x, 0, y, getCurrentPoint(), mLinePaint);
...
}
}
and from MainActivity set currentPoint value by calling setCurrentPoint method before calling onDraw:
for(int i=0; i<samples.length; i++) {
currentPoint = samples[i];
myInstance.setCurrentPoint(currentPoint);
myInstance.draw(canvas);
myInstance.invalidate();
}
Specify currentPoint as a static variable, and use MainActivity.currentPoint to get the value of the variable from your other class. Alternatively, just use a getter method that returns the variable.

Android ScrollView only scroll to bottom if you're already there

I have a basic android app set up with a scroll view and text added to it dynamically.
I want it to scroll to the bottom when text is added (which already happens) but I only want it to scroll to the bottom if you are already at the bottom, so if you're reading something it doesn't just scroll.
Here's what I have so far.
private void AddText(final String msg){
this.runOnUiThread(new Runnable() {
public void run() {
TextView log = (TextView) findViewById(R.id.chatlog);
if(log.getText().equals("Loading...")){
log.setText(msg);
}else{
ScrollView scroller = (ScrollView) findViewById(R.id.scroll_container);
//set current scroll position
int scroll_pos = scroller.getScrollY();
//scroll to bottom
scroller.fullScroll(ScrollView.FOCUS_DOWN);
//set bottom position
int scroll_bot = scroller.getScrollY();
//add the text
log.append("\r\n" + msg);
//if you weren't at the bottom
//scroll back to where you were.
//This isn't working, scroll bot is the same
//as scroll pos.
if(scroll_pos != scroll_bot){
scroller.scrollTo(0, scroll_pos);
}
//System.out.println("Pos: " + scroll_pos);
//System.out.println("Bot: " + scroll_bot);
}
}
});
}
The best solution I found so far is to use scrollView.post() method with a runnable that will be invoked after text change:
final ScrollView scrollView = (ScrollView) findViewById(R.id.consoleTab);
TextView textView = (TextView) findViewById(R.id.consoleView);
boolean autoScroll = (textView.getBottom() - (scrollView.getHeight() + scrollView.getScrollY())) <= 0;
textView.setText(state.getConsole().getText());
if (autoScroll) {
scrollView.post(new Runnable() {
public void run() {
scrollView.fullScroll(ScrollView.FOCUS_DOWN);
}
});
}
Here is a working example I got running in the emulator and on my galaxy s.
Basically there are two buttons to add text to the bottom text view, depending on your devices size only the second of these should be usable to see the autoscrolling. The textview being edited uses the ontextchangedlistener to check the scroll position before its text is changed and then to call a delayed (so the screen can update with the additional text) autoscroll where appropriate, after the text has changed.
The layout xml is as follows:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/scrollmain">
<RelativeLayout
android:id="#+id/mainRelative"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="#+id/titleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Welcome to the scrolling test application!" />
<Button
android:id="#+id/firstTextAddButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/titleText"
android:text="Click me to add text to the textview below without scrolling"/>
<Button
android:id="#+id/secondTextAddButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/firstTextAddButton"
android:layout_marginTop="380dp"
android:text="Click me to add text to the textview below and scroll (if you are currently scrolled all the way to the bottom)"/>
<TextView
android:id="#+id/textToEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/secondTextAddButton"
android:textSize="18sp"
android:text="Some text to get us started."/>
/>
</RelativeLayout>
</ScrollView>
And the code for the activity is as follows:
package code.example.scrollingontextchange;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TextView;
public class MainActivity extends Activity {
private int scroll_pos;
private int maxScrollPosition;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = (TextView)findViewById(R.id.textToEdit);
tv.addTextChangedListener(new TextWatcher(){
#Override
public void afterTextChanged(Editable s) {
if(scroll_pos == maxScrollPosition)
{
Handler h = new android.os.Handler()
{
#Override
public void handleMessage(android.os.Message msg)
{
switch(msg.what)
{
case 0 :
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ScrollView scrll = (ScrollView)findViewById(R.id.scrollmain);
scrll.fullScroll(ScrollView.FOCUS_DOWN);
break;
default :
break;
}
}
};
h.sendEmptyMessage(0);
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
ScrollView scrll = (ScrollView)findViewById(R.id.scrollmain);
RelativeLayout rl = (RelativeLayout)findViewById(R.id.mainRelative);
scroll_pos = scrll.getScrollY();
maxScrollPosition = rl.getHeight() - scrll.getHeight();
}
#Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
// TODO Auto-generated method stub
}
});
OnClickListener addTextOnClick = new OnClickListener() {
#Override
public void onClick(View v) {
TextView tv = (TextView)findViewById(R.id.textToEdit);
tv.setText(tv.getText() + "\nA long time ago, in a galaxy s far far away............");
}
};
Button b = (Button)findViewById(R.id.firstTextAddButton);
b.setOnClickListener(addTextOnClick);
Button b2 = (Button)findViewById(R.id.secondTextAddButton);
b2.setOnClickListener(addTextOnClick);
}
#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;
}
}
Hope this helps.
Don't ask me why, but setting your TextView's gravity to bottom it does exactly what you want.

Categories