Using personal SurfaceView on LinearLayout - android

I'm currently developing an Android App, using a personal SurfaceView and double buffering. However I'm facing a little problem with my code.
In one hand I have an xml view, based on LinearLayout hierarchy. When I instantiate my activity, I set my contentView on this xml. The problem is then that my double buffering don't works anymore. Thread is running but nothing is displayed.
In the other hand, I set my contentView with a new personal SurfaveView element and display works fine. But of course, I cannot access anymore to the other elements of my LinearLayout.
Actually, I would like to set my contentView on my xml view AND keep my display working.
I hope I was clear enough, thank you for your answers!
Here is my activity:
public class MainController extends Activity {
#Override
protected void onCreate(final Bundle p_savedInstanceState) {
super.onCreate(p_savedInstanceState);
setContentView(R.layout.main_controller_activity);
this.drawableView = (MCustomDrawableView) findViewById(R.id.drawingBox);
...
}
...
}
My surface view:
public class MCustomDrawableView extends SurfaceView {
public MCustomDrawableView(Context p_context, AttributeSet p_attributes) {
super(p_context, p_attributes);
this.getHolder().addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(final SurfaceHolder p_holder) {
try {
// Instantiating a new thread
thread = new MBufferingThread(getInstance());
thread.start();
} catch (Exception exception) {
exception.printStackTrace();
}
}
#Override
public void surfaceChanged(final SurfaceHolder p_holder, int p_format, int p_width, int p_height) {...}
#Override
public void surfaceDestroyed(final SurfaceHolder p_holder) {...}
});
// Setup drawing options
setupDrawing();
}
...
}
And my xml view:
<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:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
android:background="#color/app_background"
tools:context=".MainController">
<com.iskn.calligraphy.models.draw.MCustomDrawableView
android:id="#+id/drawingBox"
style="#style/DrawingBox" />
<SeekBar
android:id="#+id/brushSize"
style="#style/SizeSeekBar" />
<SeekBar
android:id="#+id/brushOpacity"
style="#style/OpacitySeekBar" />
</LinearLayout>
EDIT:
After further researches and analyses, it appears clearly that:
setContentView(R.layout.main_controller_activity): in this case I get all the elements from my activity, but the MCustomDrawableView display nothing.
setContentView(new MCustomDrawableView(getApplicationContext())): on that case, MCustomDrawableView is working well (it displays what I want), but I don't have the others View from my main_controller_activity
In both cases:
my thread is running and works well.
my drawing function is called as well, with the holder.lockCanvas() and holder.unlockCanvasAndPost(bufferCanvas) methods.

Well, I found a functionnal solution :
I think the problem was coming from the MCustomDrawableView initialization . I created a new instance of that class from the onCreate method , and not from the xml file . Then, I set it to the contentView:
protected void onCreate(Bundle p_savedInstanceState) {
super.onCreate(p_savedInstanceState);
setContentView(R.layout.main_controller_activity);
// Instantiate drawable object & set style properties
this.drawableView = new MCustomDrawableView(getApplicationContext());
FrameLayout.LayoutParams style = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT);
// Add the drawable view to the main_activity
addContentView(this.drawableView, style);
}
But this way raises a new problem. The added view is on the top, which means that all the others View are hided. I solved this with bringToBack(this.drawableView) below method:
private void bringToBack(View p_view) {
// Get parent from the current view
ViewGroup viewGroup = ((ViewGroup) p_view.getParent());
int childrenCount = viewGroup.indexOfChild(p_view);
for(int cpt = 0; cpt < childrenCount; cpt++) {
// Move the child to the top
viewGroup.bringChildToFront(viewGroup.getChildAt(cpt));
}
}
It brings back the provided view to the background position. I also had to set the LinearLayout's alpha to 0.
I'm still open to other solutions!

Related

Detect Touch on Surfaceview android

I'm creating a camera app and need to detect Touch on surfaceView which is camera preview. I want to set focus of camera where user touch. But Right i just only need to detect touch place.
Here is my preview class code.
public class Preview extends SurfaceView implements SurfaceHolder.Callback {
public Preview(Context ctx, SurfaceView surfaceView) {
super(ctx);
init(surfaceView);
}
private void init(SurfaceView surfaceView){
mSurfaceView = surfaceView;
final SurfaceHolder surfaceHolder = mSurfaceView.getHolder();
surfaceHolder.addCallback(this);
}
public void setCamera(){
mCamera = Camera.open()
mCamera.setPreviewDisplay(mSurfaceView.getHolder());
mCamera.startPreview();
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i(TAG, "onInterceptTouchEvent");
return true;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, "onTouchEvent");
return super.onTouchEvent(event);
}
// Other code.... like override methods etc
MainActivity
// inside onCreate() method
preview = new Preview(this, surfaceView);
RelativeLayout mainLayout = (RelativeLayout) findViewById(R.id.mainLayout);
mainLayout.addView(preview);
preview.setCamera();
// Other codes .....
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mainLayout">
<SurfaceView
android:id="#+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
I added onTouchEvent and onInterceptTouchEvent But both are not working. Whenever I touched on sufaceView nothing show in Log I think I'm missing some thing, can you please let me know ?
preview = new Preview(this, surfaceView);
preview.setCamera();
There is just two line code in MainActivity initilizing the Preview and setCamera() nothing else.
Why you do not see onTouchEvent and onInterceptTouchEvent logs?
I assume, that you haven't added that layout to your view hierarchy.
rootView.addView(preview);
After adding it you should see touch event logs.
But that's not what you actually expect to do, because you have just incorrectly "wrapped" your SurfaceView inside a Java class, which yet doesn't mean that touch events on your SurfaceView would be reflected in your logs.
What you really want is to extend SurfaceView and override those touch events there. Now you would receive correct touch events. Then make sure you are inflating your custom SurfaceView into view hierarchy.
Or, second approach, you can add SurfaceView as a child of your ViewGroup:
yourCustomViewGroup.addView(surfaceView);
There are a few issues with your extended class, in that it does not include all required override methods. However I will assume this is still being developed.
In theory this should work:
preview.setOnClickListener( new OnClickListener(){
#Override
public void onClick() {
//do stuff
}
});
I have not tested it, but as this is extending ViewGroup, this should work.
Note: You probably will need to add this
android:clickable="true"
To your 'Preview' layout, to allow the click to be processed.
Hope that helps.

Updating Android UI from custom view

I'm new to Android development, and still having difficulties comprehending how the framework works. I try to write a simple game to get some experience and I'd like to ask for some help with the below.
In my main Activity, I have a few TextViews to show various data from my game, and under those I have a custom View called AnimatedView that has the canvas where I draw my objects for the game itself. Based on the objects on the Canvas, I'd like to update the TextViews to show e.g. number of object, what level the player is at, etc.
The problem is, even though I can get the TextView object with the below code, when I call textView.setText(...), nothing changes on the UI.
The below is a simplified version of my real code, to demonstrate the problem:
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
...
tools:context="com.danielsh.test.teststuff.MainActivity"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/myTextView"
android:text="Change this!" />
<com.danielsh.test.teststuff.AnimatedView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/animView" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
AnimatedView.java
public class AnimatedView extends View {
public AnimatedView(Context context, AttributeSet attrs) {
super(context, attrs);
final Handler uiHandler = new Handler();
final Runnable runnable = new Runnable() {
public void run() {
LayoutInflater inflater = LayoutInflater.from(getContext());
LinearLayout layout = (LinearLayout) inflater.inflate(R.layout.activity_main, null, false);
TextView textView = (TextView) layout.findViewById(R.id.myTextView);
textView.setText("" + (System.currentTimeMillis() / 1000));
textView.invalidate();
}
};
uiHandler.postDelayed(runnable, 1000);
}
}
I assume that I'm not working with the actual TextView object that's visible on the UI, but not sure what'd be the correct way of doing that.
Any help or pointer to an existing post that deals with the same problem would be much appreciated.
It's not clear to me, what you're trying to do with AnimatedView, so I won't be able to give you a real solution on how it would be working correctly, only on what's your issue here. If this is your full implementation, you should maybe try to implement the behavior it in your activity itself.
In your posted Runnable you inflate your layout from XML. That means you create new layouts and views and assign these to your local variable. These views are not part of the layout hierarchy displayed in your activity. So when you update the text of the TextView you'll never see any changes anywhere.
As a sidenote: Use postDelayed(Runnable, long) instead of creating a new Thread.

Integrate Unity3d view into Android activity

I'm currently working on a small AR app for Android and am facing the problem of integrating Unity3d into an activity. The requirements indicate that I need to be able to present some Android UI - e.g. menus and an action bar - and a camera view that will display a model created in Unity3d when the target is detected.
I found a link that helped me a lot: Unity3d forums. One of the users there asked the same question I have now but never got any proper answer -that's why I'm posting here.
Problem:
I got a small Unity3d project that is essentially a white cube and am trying to display it in one of my Android activities. The model looks fine when activity doesn't have setContentView() in its onCreate() method but then I can't specify my layout in an XML file.
When I do add the setContentView() method, I can see the cube but it's very small and there doesn't seem to be any way of actually making it change its size.
The XML file:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/unityView"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
</FrameLayout>
1st version of the activity implementation:
public class HomeActivity extends UnityPlayerActivity {
UnityPlayer unityPlayer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
And the resulting screenshot:
2nd version of the activity implementation:
public class HomeActivity extends UnityPlayerActivity {
UnityPlayer unityPlayer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
unityPlayer = new UnityPlayer(this);
int glesMode = unityPlayer.getSettings().getInt("gles_mode", 1);
unityPlayer.init(glesMode, false);
FrameLayout layout = (FrameLayout) findViewById(R.id.unityView);
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
layout.addView(unityPlayer.getView(), 0, lp);
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
unityPlayer.windowFocusChanged(hasFocus);
}
}
And the resulting screenshot:
Could anyone explain to me why that is and how to fix it?
While I still don't know why it works like that, I've found a way of fixing it.
Instead of simply using setContentView() in onCreate(), extend onResume() and in that method recursively look through all the available views to find the parent view of the UnityPlayer object. Once that's found, layouts and other views can be inflated and added to that parent view.
Here's the link with a code example - I've used this to make my app work: https://developer.vuforia.com/resources/dev-guide/extending-unity-android-activity-and-adding-custom-views-eclipse
Edit: Here's a code snippet showing my solution.
#Override
public void onResume() {
super.onResume();
if (unityPlayer == null) {
View rootView = findViewById(android.R.id.content);
unityPlayer = findUnityPlayerView(rootView);
if (unityPlayer != null) {
ViewGroup unityPlayerParentView = (ViewGroup)(unityPlayer.getParent());
View mainHomeView = getLayoutInflater().inflate(R.layout.activity_main, null);
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
unityPlayerParentView.addView(mainHomeView, layoutParams);
}
}
}
and
private UnityPlayer findUnityPlayerView(View view) {
if (view instanceof UnityPlayer) {
return (UnityPlayer) view;
}
if (view instanceof ViewGroup) {
ViewGroup childrenViews = (ViewGroup) view;
for (int i = 0; i < childrenViews.getChildCount(); i++) {
UnityPlayer foundView = findUnityPlayerView(childrenViews.getChildAt(i));
if (foundView != null) {
return foundView;
}
}
}
return null;
}

Android: Rotated view in popup window getting clipped

I'm trying to rotate a ListView inside of a custom popupWindow. Below is my setup:
Here is the popup XML, board_dialog.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/boardll"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center">
<ListView
android:id="#+id/boardoptions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="#array/options_array_board" />
</RelativeLayout>
My custom BoardPopup class:
public class BoardPopup extends PopupWindow {
private static final String TAG = BoardPopup.class.getSimpleName();
Context context;
RelativeLayout ll;
ListView lv;
private OnSubmitListener mListener;
public BoardPopup (Context ctx, OnSubmitListener listener) {
super(ctx);
context = ctx;
mListener = listener;
setContentView(LayoutInflater.from(context).inflate(R.layout.board_dialog, null));
setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
setWidth(WindowManager.LayoutParams.WRAP_CONTENT);
View popupView = getContentView();
setFocusable(true);
lv = (ListView) popupView.findViewById(R.id.boardoptions);
ll = (RelativeLayout) popupView.findViewById(R.id.boardll);
}
public void show(View v) {
showAtLocation(v, Gravity.CENTER, 0, 0);
}
public interface OnSubmitListener {
void valueChanged(String name, String number);
}
public void fixDimensions() {
getContentView().setBackgroundColor(Color.RED); //to highlight views
ll.setRotation(90);
update(292,630); //These numbers are not meant to be constant
}
}
In my activity, showing the popup and I have to override onWindowFocusChanged in order to get post-drawn dimensions for the views inside the popup:
popup = new BoardPopup(c, MainGamePanel.this);
popupJustCreated = true;
popup.show(v);
.
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (popup!=null && popupJustCreated) {
popup.fixDimensions();
popupJustCreated = false;
}
}
If I comment out ll.setRotation(90); and update(292,630); in fixDimensions() then everything looks normal:
If I add in the ll.setRotation(90);:
Finally, if I add in the update(292,630);:
In the final image, why does the layout not fill the popup? What view is that gray area? How can I get this to rotate and resize normally?
Some other things I've tried with no success:
Using LinearLayout instead of RelativeLayout
all different combinations of wrap_content and match_parent
Doing basically the same thing with a custom DialogFragment
I had a very similar issue and just found a workaround. I was using the view rotationX property to rotate items within a RecyclerView and kept seeing strange clipping behaviour like the images above. What worked for me was calling setLayerType on the parent view (a RecyclerView in my case) with the following arguments:
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
The default layer type is is LAYER_TYPE_HARDWARE for API >= 14). By the nature of this work around I'd say this is an Android bug.

How to add view components dynamically from activity

I am attempting to create a user interface dynamically. I have successfully create a view and loaded my background image. I have created two additional small view items to display on the background. My problem is that I have not been able to find any advice/instruction that tells me how to draw the small views. It seems that it should be a trivial exercise and I am guessing it is just finding the correct referencing. Hope someone out there can point me in the right direction.
Here is my Activity:
public class GhostActivity extends Activity implements OnTouchListener
{
private DrawView ghostView;
public Card mCard1, mCard2;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
// ToDo add your GUI initialization code here
super.onCreate(savedInstanceState);
// requesting to turn the title OFF
requestWindowFeature(Window.FEATURE_NO_TITLE);
// making it full screen
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
ghostView = new DrawView(this);
setContentView(ghostView);
//get the window size
Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
Context context = getApplicationContext();
//create view items with initial positions
Point startPoint;
startPoint = new Point();
startPoint.x = 5;
startPoint.y = 3;
mCard1 = new Card(context, 1, R.drawable.bol_geel, startPoint);
startPoint.x = 5;
startPoint.y = 43;
mCard2 = new Card(context, 2, R.drawable.bol_rood, startPoint);
//now display them on the ghostView *****************HOW?
// set the callbacks
ghostView.setOnTouchListener(this);
mCard1.setOnTouchListener(this);
mCard2.setOnTouchListener(this);
}
and here is the View;
public class DrawView extends View
{
Drawable bg ;
public DrawView(Context context) {
super(context);
//setFocusable(true);
Drawable bg = this.getResources().getDrawable(R.drawable.bubbleblue480x800);
setBackgroundDrawable(bg);
}
#Override protected void onDraw(Canvas canvas) {
// canvas.drawColor(0x0000000); //if you want another background color
//draw on the canvas
}
}
edit: I believe my problem is needing to pass a pointer to the ghostView canvas. what makes me think that is if I create the children within ghostView then call their .draw method they appear exactly as I would expect.
#Override protected void onDraw(Canvas canvas) {
canvas.drawColor(0x0000000); //if you want another background color
//draw the cards on the canvas
mCard1.draw(canvas);
mCard2.draw(canvas);
}
so at this point I am wondering how to get a reference pointer to the ghostView canvas.
To be honest I am finding the whole Activity - View relationship confusing.
Edit: I have taken a different approach based on detail in this tutorial
http://www.kellbot.com/2009/06/android-hello-circle/
It uses a FrameLayout and it seems I can achieve my objective.
To add view dynamically to view your class must extends from ViewGroup or LinearLayout class then you will able to call method addView.
Inside your ghost view first add a layout e.g Linear or Relative. Then only you could able to add views inside that layout you cant simply add a view to a xml file.
Or you can create a dynamic layout then only u can add view inside that layout.
RelativeLayout relative= new RelativeLayout(findViewById(R.id.your relativeLayoutID));
relative.addView(child);
child could be anything button textview and widget.

Categories

Resources