This may be a very stupid question (which it's got like 99% chance it is) but I can't figure this out for the life of me. I've got a seekbar in one activity, we'll call it Activity 1, and I need to use it's constantly changing value (progress) in another activity, we'll call it Activity 2, for calculations (or maybe I can do the calculations in the first activity and then just send the value over to the second one, IDK which would be better).
So here's my Activity 1 code:
public class Painting extends Activity
{
SeekBar curveBar;
SampleView sampleView;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_painting);
curveBar = (SeekBar)findViewById(R.id.curveBar);
sampleView = new SampleView(this);
curveBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
int progressChangedValue = 0;
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (progress >= 50) //prints out 50, 60, 70, 80, 90, 100
{
progressChangedValue = progress;
}
else if (progress < 50) //prints out -40, -30, -20, -10, 0
{
progressChangedValue = (-1) * (progress);
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
Toast.makeText(Painting.this, "Value: " + progressChangedValue, Toast.LENGTH_SHORT).show();
}
});
}
}
and here's my RELEVANT Activity 2 code:
public class SampleView extends View
{
private Paint mPaint;
private float mX;
private float[] mPos;
private Path mPath;
private Paint mPathPaint;
private static final int DY = 30;
private static final String TEXTONPATH = "Along a path";
public SampleView(Context context, AttributeSet attrs)
{
super(context, attrs);
setFocusable(true);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setTextSize(90);
mPaint.setTypeface(Typeface.SERIF);
mPath = new Path();
makePath(mPath);
mPathPaint = new Paint();
mPathPaint.setAntiAlias(true);
mPathPaint.setColor(Color.rgb(255,0,0));
mPathPaint.setStyle(Paint.Style.STROKE);
}
public SampleView(Context context)
{
super(context);
setFocusable(true);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setTextSize(90);
mPaint.setTypeface(Typeface.SERIF);
mPath = new Path();
makePath(mPath);
mPathPaint = new Paint();
mPathPaint.setAntiAlias(true);
mPathPaint.setColor(Color.rgb(255,0,0));
mPathPaint.setStyle(Paint.Style.STROKE);
}
public void makePath(Path p) //<----- this is where i need the seekbar's value
//so that i can make this take more variables so that the seekbar
//adjusts the values in the p.cubicTo's below
{
// p.moveTo(250, -300);
p.moveTo(0,0);
// p.cubicTo(-250, -550, 750, -550, 250, -300);
p.cubicTo(0,-400,600,-400,600,0); //semi-circle?
// p.cubicTo(-600, -400, 600, -400, 0, 0); //as far as the curve probably should allow
// p.cubicTo(0, 0, 0, 0, 620,0); //flat line
}
I have tried to use curvebar.getProgress() on both the first activity and the second activity but I can't get it unless I'm within the OnSekbarChangeListener. I've tried setting public variables to the value but it only changes if i set the value within the listener, otherwise it doesn't (which makes perfect sense). I've tried setting the seekbar to be static (although I probably didn't do it right) and that didn't work either. I just can't figure out how i can get that value out and use it in the second file.
I would really appreciate some help
Thank you
Just create a public method in your SampleView class that can take the seekbar's progress as a parameter.
public class SampleView extends View {
...
public void updatePath(int seekBarProgress) {
// Do whatever you need to with the passed in value here.
}
...
}
Then in your Activity you can call this method to update the SampleView with the latest progress value.
public class Painting extends Activity {
...
curveBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
...
sampleView.updatePath(progress);
...
}
...
}
}
Related
I've been trying to find solution of this issue, but I haven't. The question is to return to Activity from View.
As soon as if statement become false, it should return me to SplashActivity
Here some Activities and View class, that perform all logic of application.
public class MainActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
GameEngine gameEngine = new GameEngine(this);
setContentView(gameEngine);
}
}
Then goes my SplashActivity with one ImageButton which call MainActivity as soon as it's pressed.
public class SplashActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
}
//this method mentioned (onClick) in XML file I've not included.
public void startGame(View view)
{
Intent mainIntent = new Intent(SplashActivity.this, MainActivity.class);
startActivity(mainIntent);
finish();
}
}
My View Class:
public class GameEngine extends View
{
boolean inGame;
//Here some code...
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
if(inGame)
{
canvas.drawBitmap(someImage, positionX, positionY, null);
if(positionX > 100)
{
inGame = false;
//Here I want to return to SplashActivity, where my ImageButton is!
}
}
}
}
I don't know how to implement idea of returning to Activity and begin using my app from innitial point.
Thanks in advance!
EDIT
This information is beside the point!
Innitialy I changed code for better readability to touch on the main point.
This is inner class where it's updating:
public static class Drawable
{
private int x;
private int y;
Drawable(int x, int y)
{
this.x = x;
this.y = y;
}
public int getX()
{
return x;
}
public int getY()
{
return y;
}
void update() {
x -= 3;
}
}
This is how myobjects actually draw
for (Drawable drawable : drawables)
{
//Draw upper pipe
canvas.drawBitmap(pipeUp, drawable.getX(), drawable.getY(), null);
//Draw lower pipe
canvas.drawBitmap(pipeBot, drawable.getX(), drawable.getY() +
pipeUp.getHeight() + GAP, null);
}
This is how it updates
private void animation()
{
for(Drawable drawable : new ArrayList<>(drawables))
{
drawable.update();
if(drawable.getX() == dWidth/3 + dWidth/3)
{
drawables.add(new Drawable(pipeX, (int)(Math.random() *
(pipeUp.getHeight() -((pipeUp.getHeight() * 0.25))) - (pipeUp.getHeight() - (pipeUp.getHeight() * 0.25)))));
}
if(drawable.getX() <= 0 - pipeBot.getWidth())
{
drawables.remove(drawable);
}
}
}
Then I call animation() in onDraw method.
You can try:
Intent mainIntent = new Intent(getContext(), SplashActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
getContext().startActivity(mainIntent);
or you can save reference of MainActivity in GameEngine's constructor and use it to open splash:
GameEngine gameEngine = new GameEngine(this);
or you can implement a livedata with a basic repository pattern:
public class DataRepository {
public static MutableLiveData<Boolean> openSplash = new MutableLiveData<>();
}
On MainActivity:
DataRepository.openSplash.observe(this, aBoolean -> {
if(aBoolean){
//open splash
}else{
//
}
});
On GameEngine:
DataRepository.openSplash.postValue(true);
I have this animation activity for falling images. It works perfectly. What I would like to is change this, so I can call something like, startImageFallAnimation(), in another activity, and have it show over the current activity. I'd hate to have to add all this code to every activity I want to use it in. I experimented for a few hours, with no luck.
How can I accomplish this?
import com.tmp.animation.R;
public class FallAnimationActivity extends Activity {
// 100 = lots falling / 1000 = less falling
public int imageInterval = 100;
private int[] LEAVES = {
R.drawable.coin,
R.drawable.starsm,
//R.drawable.leaf_yellow,
//R.drawable.leaf_other,
};
private Rect mDisplaySize = new Rect();
private RelativeLayout mRootLayout;
private ArrayList<View> mAllImageViews = new ArrayList<View>();
private float mScale;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Display display = getWindowManager().getDefaultDisplay();
display.getRectSize(mDisplaySize);
DisplayMetrics metrics = new DisplayMetrics();
display.getMetrics(metrics);
mScale = metrics.density;
mRootLayout = (RelativeLayout) findViewById(R.id.main_layout);
new Timer().schedule(new ExeTimerTask(), 0, imageInterval);
}
public void create() {
setContentView(R.layout.main);
Display display = getWindowManager().getDefaultDisplay();
display.getRectSize(mDisplaySize);
DisplayMetrics metrics = new DisplayMetrics();
display.getMetrics(metrics);
mScale = metrics.density;
mRootLayout = (RelativeLayout) findViewById(R.id.main_layout);
new Timer().schedule(new ExeTimerTask(), 0, imageInterval);
}
public void startAnimation(final ImageView aniView) {
aniView.setPivotX(aniView.getWidth()/2);
aniView.setPivotY(aniView.getHeight()/2);
long delay = new Random().nextInt(Constants.MAX_DELAY);
final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
animator.setDuration(Constants.ANIM_DURATION);
animator.setInterpolator(new AccelerateInterpolator());
animator.setStartDelay(delay);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
int angle = 50 + (int)(Math.random() * 101);
int movex = new Random().nextInt(mDisplaySize.right);
#Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = ((Float) (animation.getAnimatedValue())).floatValue();
aniView.setRotation(angle*value);
aniView.setTranslationX((movex-40)*value);
aniView.setTranslationY((mDisplaySize.bottom + (150*mScale))*value);
}
});
animator.start();
}
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
int viewId = new Random().nextInt(LEAVES.length);
Drawable d = getResources().getDrawable(LEAVES[viewId]);
LayoutInflater inflate = LayoutInflater.from(FallAnimationActivity.this);
ImageView imageView = (ImageView) inflate.inflate(R.layout.ani_image_view, null);
imageView.setImageDrawable(d);
mRootLayout.addView(imageView);
mAllImageViews.add(imageView);
LayoutParams animationLayout = (LayoutParams) imageView.getLayoutParams();
animationLayout.setMargins(0, (int)(-150*mScale), 0, 0);
animationLayout.width = (int) (60*mScale);
animationLayout.height = (int) (60*mScale);
startAnimation(imageView);
}
};
private class ExeTimerTask extends TimerTask {
#Override
public void run() {
// we don't really use the message 'what' but we have to specify something.
mHandler.sendEmptyMessage(Constants.EMPTY_MESSAGE_WHAT);
}
}
}
EDIT- After lots of work, this is the best I've got, but I cant solve passing context into the handler, or passing the layout into the first method.
import com.tmp.animation.R;
public class FallPop {
private static final String TAG = FallPop.class.toString();
private static final FallPop INSTANCE = new FallPop();
private int[] LEAVES = {
R.drawable.leaf_green,
R.drawable.leaf_red,
R.drawable.leaf_yellow,
R.drawable.leaf_other,
};
private Rect mDisplaySize = new Rect();
private RelativeLayout mRootLayout;
private ArrayList<View> mAllImageViews = new ArrayList<View>();
private float mScale;
private FallPop(){
}
public static FallPop getInstance() {
return INSTANCE;
}
public Context context;
public Context context2;
int count = 0;
public void doAnim(Context context){
WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
display.getRectSize(mDisplaySize);
DisplayMetrics metrics = new DisplayMetrics();
display.getMetrics(metrics);
mScale = metrics.density;
// FIX!!!
// mRootLayout = (RelativeLayout) findViewById(R.id.main_layout);
new Timer().schedule(new ExeTimerTask(), 0, 200);
}
public void startAnimation(final ImageView aniView) {
aniView.setPivotX(aniView.getWidth()/2);
aniView.setPivotY(aniView.getHeight()/2);
long delay = new Random().nextInt(Constants.MAX_DELAY);
final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
animator.setDuration(Constants.ANIM_DURATION);
animator.setInterpolator(new AccelerateInterpolator());
animator.setStartDelay(delay);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
int angle = 50 + (int)(Math.random() * 101);
int movex = new Random().nextInt(mDisplaySize.right);
#Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = ((Float) (animation.getAnimatedValue())).floatValue();
aniView.setRotation(angle*value);
aniView.setTranslationX((movex-40)*value);
aniView.setTranslationY((mDisplaySize.bottom + (150*mScale))*value);
}
});
animator.start();
}
Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
int viewId = new Random().nextInt(LEAVES.length);
// Need some context here \/
Drawable d = ContextCompat.getDrawable(context, LEAVES[viewId]);
// Original line, also didnt work \/
//Drawable d = getResources().getDrawable(LEAVES[viewId]);
LayoutInflater inflate = LayoutInflater.from(context);
ImageView imageView = (ImageView) inflate.inflate(R.layout.ani_image_view, null);
imageView.setImageDrawable(d);
mRootLayout.addView(imageView);
mAllImageViews.add(imageView);
RelativeLayout.LayoutParams animationLayout = (RelativeLayout.LayoutParams) imageView.getLayoutParams();
animationLayout.setMargins(0, (int) (-150 * mScale), 0, 0);
animationLayout.width = (int) (60 * mScale);
animationLayout.height = (int) (60 * mScale);
startAnimation(imageView);
}
};
class ExeTimerTask extends TimerTask {
#Override
public void run() {
// we don't really use the message 'what' but we have to specify something.
mHandler.sendEmptyMessage(Constants.EMPTY_MESSAGE_WHAT);
}
}
}
Create a Java class with static method startImageFallAnimation(),where you will write all your animation code and just call the method wherever it is required
The quickest solution I can think of would be to make the target Activities extend this class and change the access level of the member variables in FallAnimationActivity to 'protected'. Depending on whether you need this in all/most Activities, I'd put the logic in a base class.
have context as a parameter in your util class
example:
public animation(Imageview imageview, Context mContext)
{
Animation slideLeft = AnimationUtils.loadAnimation(mContext, R.anim.slide_out_left);
imageview.startAnimation(slideLeft);
}
then in the activity you want to call it in do
// for activity
Utils.animation(Your image view, this)
//for fragment
Utils.animation(Your image view, getContext)
my utility class is called Utils, so you type whatever you named the class and call the method accordingly
This is my first attempt at an Android game. I need to draw a path that starts at the center of the screen, and I'm getting the center screen coordinates using OnGlobalLayoutListener. In order to pass the center coordinates to my View class I'm using an Interface. Now, assuming that the Interface is working, it's passing 0,0 because the drawn path is starting at the upper left of my Relative Layout. I'm getting the correct coordinates because for now I'm passing them to a TextView so I can see the coordinates, but they're not getting passed to the View class because the path starts in the upper left, not the center of my Relative Layout. So I guess my question is: Does the Interface get triggered before onCreate completes? And if so, how can I get the center screen data to the Interface before the Interface is triggered? Can I somehow trigger the Interface after I get the screen data? Here are the classes I'm working with, first the Main Activity:
public class Game1 extends Activity implements GBoardValueSetter {
DrawPath dp;
TextView gameScore_TV, gameTime_TV;
private CountDownTimer mainGameCD;
private boolean mainGameTimerHasStarted = false;
private final long mainCDTime = 30 * 1000;
private final long mainCDInterval = 1 * 1000;
// ---GameBoard variables
RelativeLayout root_RL, gBoardInfo_RL, gBoard_RL;
LayoutInflater LayInf = null;
View root_VUE, gBoardInfo_VUE;
LayoutParams root_LP, gBoardInfo_LP, gBoard_LP;
int xScr2Int, yScr2Int, xScrInt, yScrInt, xCenterInt, yCenterInt;
String xCenterStr, yCenterStr, xHStr, yWStr;
float xCenter_FL, yCenter_FL;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
overridePendingTransition(R.anim.cutin, R.anim.cutout);
root_RL = new RelativeLayout(this);
root_LP = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.MATCH_PARENT);
root_RL.setId(1);
root_RL.setBackgroundColor(Color.WHITE);
root_RL.setLayoutParams(root_LP);
setContentView(root_RL);
createBlankGBoard();
addDP();
}// --- END onCreate
//--- Interface call
#Override
public void passGBCenterIntValuesInterface(int xC_IInt, int yC_IInt) {
xC_IInt = xCenterInt;
yC_IInt = yCenterInt;
dp.setCenterInt(xC_IInt, yC_IInt);
}//--- END Interface call
public void createBlankGBoard(){
LayInf = (LayoutInflater) getApplicationContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
gBoardInfo_VUE = LayInf.inflate(R.layout.game1_info, null);
gBoardInfo_LP = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT);
gBoardInfo_LP.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE);
gBoardInfo_VUE.setId(2);
gBoardInfo_VUE.setLayoutParams(gBoardInfo_LP);
gameTime_TV = (TextView) gBoardInfo_VUE.findViewById(R.id.game1_timeValue2_TV);
gameScore_TV = (TextView) gBoardInfo_VUE.findViewById(R.id.game1_scoreValue2_TV);
root_RL.addView(gBoardInfo_VUE);
gBoard_RL = new RelativeLayout(this);
gBoard_LP = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.MATCH_PARENT);
gBoard_LP.addRule(RelativeLayout.BELOW, gBoardInfo_VUE.getId());
gBoard_LP.setMargins(10, 10, 10, 10);
gBoard_RL.setLayoutParams(gBoard_LP);
gBoard_RL.setBackgroundColor(Color.MAGENTA);
root_RL.addView(gBoard_RL);
getGameBoardNumbers();
}
public void addDP(){
root_RL.removeView(gBoard_RL);
dp = new DrawPath(this);
gBoard_RL.setBackgroundColor(Color.CYAN);
gBoard_RL.addView(dp);
root_RL.addView(gBoard_RL);
}
//-- Get GameBoard info using oGLL
public void getGameBoardNumbers(){
gBoard_RL.getViewTreeObserver().addOnGlobalLayoutListener(
NumbersoGLL);
}
//-- oGLL
OnGlobalLayoutListener NumbersoGLL = new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
xScrInt = gBoard_RL.getWidth();
yScrInt = gBoard_RL.getHeight();
xScr2Int = gBoard_RL.getWidth()/2;
yScr2Int = gBoard_RL.getHeight()/2;
xCenterInt = gBoard_RL.getLeft() + xScr2Int;
yCenterInt = gBoard_RL.getTop() + yScr2Int;
xHStr = String.valueOf(xScrInt);
yWStr = String.valueOf(yScrInt);
xCenter_FL = (float)xCenterInt;
yCenter_FL = (float)yCenterInt;
String xCInt_Str = String.valueOf(xCenterInt);
String yCInt_Str = String.valueOf(yCenterInt);
gameScore_TV.setText(xCInt_Str + ", " + yCInt_Str);
}
};
}
Here's the View Class:
public class DrawPath extends View {
Paint paint = new Paint();
Path path = new Path();
private int xC_Int, yC_Int;
class Pt {
float x, y;
Pt(float _x, float _y) {
x = _x;
y = _y;
}
}
public void setCenterInt(int xC_IInt, int yC_IInt){
this.xC_Int = xC_IInt;
this.yC_Int = yC_IInt;
}
Pt[] thePath = { new Pt(xC_Int, yC_Int),
new Pt(200, 200),
new Pt(200, 500),
new Pt(400, 500)
};
public DrawPath(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(Color.RED);
paint.setStrokeWidth(7);
paint.setStyle(Paint.Style.STROKE);
path.moveTo(thePath[0].x, thePath[0].y);
for (int i = 1; i < thePath.length; i++) {
path.lineTo(thePath[i].x, thePath[i].y);
}
canvas.drawPath(path, paint);
}
}
And the Interface Class:
public interface GBoardValueSetter {
void passGBCenterIntValuesInterface(int xC_IInt, int yC_IInt);
}
In your interface call to your activity, you assign the parameters you get from the call:
public void passGBCenterIntValuesInterface(int xC_IInt, int yC_IInt) {
xC_IInt = xCenterInt; <-- RIGHT THERE
yC_IInt = yCenterInt; <-- RIGHT THERE
dp.setCenterInt(xC_IInt, yC_IInt);
}//--- END Interface call
xCenterInt and yCenterInt are problably 0
In my app I have a basic compass which is rendered in a "compassView" Class. If I set the contentView in my Display Compass activity like so: compassView = new CompassView(this); setContentView(compassView); it works fine and points north however if I set the content view to setContentView(activity_display_compass) it will render the compass and any textviews I may have but the compass is static; ie the needle does not move. I am at loss to what is causing this. Any guidance would be much appreciated?
xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world"/>
<com.example.gpsfinder.CompassView
android:id="#+id/compassView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
Display Compass Activity
public class DisplayCompass extends Activity {
private static SensorManager sensorService;
private CompassView compassView;
private Sensor sensorAccelerometer;
private Sensor sensorMagnetometer;
private float [] lastAccelerometer = new float[3];
private float [] lastMagnetometer = new float[3];
private boolean lastAccelerometerSet = false;
private boolean lastMagnetometerSet = false;
private float[] rotation = new float[9];
private float[] orientation = new float[3];
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
compassView = new CompassView(this);
setContentView(compassView);
sensorService = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensorAccelerometer = sensorService.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorMagnetometer = sensorService.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
//sensor = sensorService.getDefaultSensor(Sensor.TYPE_ORIENTATION);
if (sensorAccelerometer != null && sensorMagnetometer != null) {
sensorService.registerListener(mySensorEventListener, sensorAccelerometer,
SensorManager.SENSOR_DELAY_UI);
sensorService.registerListener(mySensorEventListener, sensorMagnetometer,
SensorManager.SENSOR_DELAY_UI);
Log.i("Compass MainActivity", "Registerered for ORIENTATION Sensor");
} else {
Log.e("Compass MainActivity", "Registerered for ORIENTATION Sensor");
Toast.makeText(this, "ORIENTATION Sensor not found",
Toast.LENGTH_LONG).show();
finish();
}
}
private SensorEventListener mySensorEventListener = new SensorEventListener() {
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
public void onSensorChanged(SensorEvent event) {
if(event.sensor == sensorAccelerometer){
System.arraycopy(event.values,0,lastAccelerometer, 0,event.values.length);
lastAccelerometerSet= true;
}
else if (event.sensor == sensorMagnetometer){
System.arraycopy(event.values,0,lastMagnetometer, 0,event.values.length);
lastMagnetometerSet = true;
}
if(lastAccelerometerSet && lastMagnetometerSet)
SensorManager.getRotationMatrix(rotation,null,lastAccelerometer ,lastMagnetometer );
SensorManager.getOrientation(rotation,orientation);
// angle between the magnetic north direction
// 0=North, 90=East, 180=South, 270=West
double azimuth = Math.toDegrees(orientation[0]);
compassView.updateDirection(azimuth);
}
};
#Override
protected void onDestroy() {
super.onDestroy();
sensorService.unregisterListener(mySensorEventListener);
}
}
Compass View
public class CompassView extends View{
private Paint paint;
private double position = 0;
public CompassView(Context context) {
super(context, null);
init();
}
public CompassView(Context context, AttributeSet attrs) {
super(context,attrs, 0);
init();
}
public CompassView(Context context, AttributeSet attrs,int defStyle) {
super(context,attrs, defStyle);
init();
}
private void init() {
paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(5);
paint.setTextSize(25);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.RED);
}
#Override
protected void onDraw(Canvas canvas) {
int xPoint = getMeasuredWidth() / 2;
int yPoint = getMeasuredHeight() / 2;
float radius = (float) (Math.max(xPoint, yPoint) * 0.6);
canvas.drawCircle(xPoint, yPoint, radius, paint);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);
// 3.143 is a good approximation for the circle
canvas.drawLine(xPoint,
yPoint,
(float) (xPoint + radius
* Math.sin((double) (-position) / 180 * 3.143)),
(float) (yPoint - radius
* Math.cos((double) (-position) / 180 * 3.143)), paint);
canvas.drawText(String.valueOf(position), xPoint, yPoint, paint);
}
public void updateDirection(double azimuth) {
this.position = azimuth;
invalidate();
}
}
Your problem was simply that when you first coded the app, you were programmatically creating your CompassView with this:
compassView = new CompassView(this);
setContentView(compassView);
When you changed to use XML layouts, that first line was presumably removed. But, you still need to assign your compassView member variable, because you use it in your SensorEventListener's onSensorChanged() method. You were getting a NullPointerException (which you can see by looking at LogCat).
So, in your onCreate() method, after setting the content view, remember to do this:
setContentView(R.layout.activity_display_compass);
compassView = (CompassView)findViewById(R.id.compassView); // <- missing?!
I have this code:
public class CopyOfLinearLayoutEntry extends LinearLayout implements Checkable {
private CheckedTextView _checkbox;
private Context c;
public CopyOfLinearLayoutEntry(Context context) {
super(context);
this.c = context;
setWillNotDraw(false);
}
public CopyOfLinearLayoutEntry(Context context, AttributeSet attrs) {
super(context, attrs);
this.c = context;
setWillNotDraw(false);
}
#Override
protected void onDraw(Canvas canvas) {
Paint strokePaint = new Paint();
strokePaint.setARGB(200, 255, 230, 230);
strokePaint.setStyle(Paint.Style.STROKE);
strokePaint.setStrokeWidth(12);
Rect r = canvas.getClipBounds();
Rect outline = new Rect(1, 1, r.right - 1, r.bottom - 1);
canvas.drawLine(r.left, r.top, r.right, r.top, strokePaint);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
// find checked text view
int childCount = getChildCount();
for (int i = 0; i < childCount; ++i) {
View v = getChildAt(i);
if (v instanceof CheckedTextView) {
_checkbox = (CheckedTextView) v;
}
}
}
#Override
public boolean isChecked() {
return _checkbox != null ? _checkbox.isChecked() : false;
}
#Override
public void setChecked(boolean checked) {
if (_checkbox != null) {
_checkbox.setChecked(checked);
}
}
#Override
public void toggle() {
if (_checkbox != null) {
_checkbox.toggle();
}
}
}
Now I also need a version for RelativeLayout, so I would duplicate the class file and replace "extends LinearLayout" with "extends RelativeLayout". I think that would be bad, because I do not want any duplicate code.
How would I go about achieving my goal, seeing that Java does not allow multiple inheritance?
I read something about the composition design pattern, but I am not sure how to implement that.
Maybe someone could give me a starting point as to how to most elegantly solve this problem?
You don't need to extend both to avoid duplicate code. You can do something like this:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckedTextView;
public class GenericLayout extends ViewGroup{
private CheckedTextView _checkbox;
public GenericLayout(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
#Override
protected void onDraw(Canvas canvas) {
Paint strokePaint = new Paint();
strokePaint.setARGB(200, 255, 230, 230);
strokePaint.setStyle(Paint.Style.STROKE);
strokePaint.setStrokeWidth(12);
Rect r = canvas.getClipBounds();
Rect outline = new Rect(1, 1, r.right - 1, r.bottom - 1);
canvas.drawLine(r.left, r.top, r.right, r.top, strokePaint);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
// find checked text view
int childCount = getChildCount();
for (int i = 0; i < childCount; ++i) {
View v = getChildAt(i);
if (v instanceof CheckedTextView) {
_checkbox = (CheckedTextView) v;
}
}
}
public boolean isChecked() {
return _checkbox != null ? _checkbox.isChecked() : false;
}
public void setChecked(boolean checked) {
if (_checkbox != null) {
_checkbox.setChecked(checked);
}
}
public void toggle() {
if (_checkbox != null) {
_checkbox.toggle();
}
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
}
}
public class Linear extends LinearLayout {
GenericLayout generic;
public Linear(Context context) {
super(context);
// TODO Auto-generated constructor stub
generic = new GenericLayout(context);
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
generic.onDraw(canvas);
}
...
}
public class Relative extends RelativeLayout{
GenericLayout generic;
public Relative(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
generic.onDraw(canvas);
}
...
}
Of what I have learned and been using, there are two ways:
You can do what you are trying to avoid (duplicate the class file and replace "extends LinearLayout" with "extends RelativeLayout")
You can create 2 interfaces and 1 class: One interface that extends LinearLayout, another one for extending RelativeLayout and the class implementing the methods and variables of the extending interfaces.
I hope that helps a little
You have to rethink your approach.
Seems like you are are using layout to control VIEW logic. Unfortunately your question does not have too much information about what you are trying to achieve.
You have few possibilities:
implement LAYOUT proxy / delegate with the custom logic (bad approach IMO)
make a dedicated HANDLER class to control your VIEW objects... these will be independent on the LAYOUT
make your VIEW object and use VIEW object instead of LAYOUT (probably the way to go)