I downloaded mediapipe for android and inside it are android module files mediapipe_repo\mediapipe\mediapipe\examples\android\solutions\hands.
In the 'hands' of mediapipe-solution-example.
enter image description here
This is a question about MainActivity.
Because I want to put the screen horizontally, I want to rotate the camera preview screen like rotating the image through the rotateBitmap function But I don't know how.
enter image description here
I really want to cry..
The following is the MainActivity with the video-related code removed from the original MainActivity.
package com.google.mediapipe.examples.hands;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import com.google.mediapipe.formats.proto.LandmarkProto.Landmark;
import com.google.mediapipe.formats.proto.LandmarkProto.NormalizedLandmark;
import com.google.mediapipe.solutioncore.CameraInput;
import com.google.mediapipe.solutioncore.SolutionGlSurfaceView;
import com.google.mediapipe.solutioncore.VideoInput;
import com.google.mediapipe.solutions.hands.HandLandmark;
import com.google.mediapipe.solutions.hands.Hands;
import com.google.mediapipe.solutions.hands.HandsOptions;
import com.google.mediapipe.solutions.hands.HandsResult;
import java.io.IOException;
import java.io.InputStream;
/** Main activity of MediaPipe Hands app. */
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Hands hands;
// Run the pipeline and the model inference on GPU or CPU.
private static final boolean RUN_ON_GPU = true;
private enum InputSource {
UNKNOWN,
IMAGE,
CAMERA,
}
private InputSource inputSource = InputSource.UNKNOWN;
// Image demo UI and image loader components.
private ActivityResultLauncher<Intent> imageGetter;
private HandsResultImageView imageView;
// Live camera demo UI and camera components.
private CameraInput cameraInput;
private SolutionGlSurfaceView<HandsResult> glSurfaceView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupStaticImageDemoUiComponents();
//Live camera
setupLiveDemoUiComponents();
}
#Override
protected void onResume() {
super.onResume();
if (inputSource == InputSource.CAMERA) {
// Restarts the camera and the opengl surface rendering.
cameraInput = new CameraInput(this);
cameraInput.setNewFrameListener(textureFrame -> hands.send(textureFrame));
glSurfaceView.post(this::startCamera);
glSurfaceView.setVisibility(View.VISIBLE);
}
}
#Override
protected void onPause() {
super.onPause();
if (inputSource == InputSource.CAMERA) {
glSurfaceView.setVisibility(View.GONE);
cameraInput.close();
}
}
private Bitmap downscaleBitmap(Bitmap originalBitmap) {
double aspectRatio = (double) originalBitmap.getWidth() / originalBitmap.getHeight();
int width = imageView.getWidth();
int height = imageView.getHeight();
if (((double) imageView.getWidth() / imageView.getHeight()) > aspectRatio) {
width = (int) (height * aspectRatio);
} else {
height = (int) (width / aspectRatio);
}
return Bitmap.createScaledBitmap(originalBitmap, width, height, false);
}
#RequiresApi(api = Build.VERSION_CODES.N)
private Bitmap rotateBitmap(Bitmap inputBitmap, InputStream imageData) throws IOException {
int orientation =
new ExifInterface(imageData)
.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
if (orientation == ExifInterface.ORIENTATION_NORMAL) {
return inputBitmap;
}
Matrix matrix = new Matrix();
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
matrix.postRotate(90);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
matrix.postRotate(180);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
matrix.postRotate(270);
break;
default:
matrix.postRotate(0);
}
return Bitmap.createBitmap(
inputBitmap, 0, 0, inputBitmap.getWidth(), inputBitmap.getHeight(), matrix, true);
}
/** Sets up the UI components for the static image demo. */
private void setupStaticImageDemoUiComponents() {
// The Intent to access gallery and read images as bitmap.
imageGetter =
registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
Intent resultIntent = result.getData();
if (resultIntent != null) {
if (result.getResultCode() == RESULT_OK) {
Bitmap bitmap = null;
try {
bitmap =
downscaleBitmap(
MediaStore.Images.Media.getBitmap(
this.getContentResolver(), resultIntent.getData()));
} catch (IOException e) {
Log.e(TAG, "Bitmap reading error:" + e);
}
try {
InputStream imageData =
this.getContentResolver().openInputStream(resultIntent.getData());
bitmap = rotateBitmap(bitmap, imageData);
} catch (IOException e) {
Log.e(TAG, "Bitmap rotation error:" + e);
}
if (bitmap != null) {
hands.send(bitmap);
}
}
}
});
Button loadImageButton = findViewById(R.id.button_load_picture);
loadImageButton.setOnClickListener(
v -> {
if (inputSource != InputSource.IMAGE) {
stopCurrentPipeline();
setupStaticImageModePipeline();
}
// Reads images from gallery.
Intent pickImageIntent = new Intent(Intent.ACTION_PICK);
pickImageIntent.setDataAndType(MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*");
imageGetter.launch(pickImageIntent);
});
imageView = new HandsResultImageView(this);
}
/** Sets up core workflow for static image mode. */
private void setupStaticImageModePipeline() {
this.inputSource = InputSource.IMAGE;
// Initializes a new MediaPipe Hands solution instance in the static image mode.
hands =
new Hands(
this,
HandsOptions.builder()
.setStaticImageMode(true)
.setMaxNumHands(2)
.setRunOnGpu(RUN_ON_GPU)
.build());
// Connects MediaPipe Hands solution to the user-defined HandsResultImageView.
hands.setResultListener(
handsResult -> {
logWristLandmark(handsResult, /*showPixelValues=*/ true);
imageView.setHandsResult(handsResult);
runOnUiThread(() -> imageView.update());
});
hands.setErrorListener((message, e) -> Log.e(TAG, "MediaPipe Hands error:" + message));
// Updates the preview layout.
FrameLayout frameLayout = findViewById(R.id.preview_display_layout);
frameLayout.removeAllViewsInLayout();
imageView.setImageDrawable(null);
frameLayout.addView(imageView);
imageView.setVisibility(View.VISIBLE);
}
/** Sets up the UI components for the live demo with camera input. */
private void setupLiveDemoUiComponents() {
Button startCameraButton = findViewById(R.id.button_start_camera);
startCameraButton.setOnClickListener(
v -> {
if (inputSource == InputSource.CAMERA) {
return;
}
stopCurrentPipeline();
setupStreamingModePipeline(InputSource.CAMERA);
});
}
/** Sets up core workflow for streaming mode. */
private void setupStreamingModePipeline(InputSource inputSource) {
this.inputSource = inputSource;
// Initializes a new MediaPipe Hands solution instance in the streaming mode.
hands =
new Hands(
this,
HandsOptions.builder()
.setStaticImageMode(false)
.setMaxNumHands(2)
.setRunOnGpu(RUN_ON_GPU)
.build());
hands.setErrorListener((message, e) -> Log.e(TAG, "MediaPipe Hands error:" + message));
if (inputSource == InputSource.CAMERA) {
cameraInput = new CameraInput(this);
cameraInput.setNewFrameListener(textureFrame -> hands.send(textureFrame));
}
// Initializes a new Gl surface view with a user-defined HandsResultGlRenderer.
glSurfaceView =
new SolutionGlSurfaceView<>(this, hands.getGlContext(), hands.getGlMajorVersion());
glSurfaceView.setSolutionResultRenderer(new HandsResultGlRenderer());
glSurfaceView.setRenderInputImage(true);
hands.setResultListener(
handsResult -> {
logWristLandmark(handsResult, /*showPixelValues=*/ false);
glSurfaceView.setRenderData(handsResult);
glSurfaceView.requestRender();
});
// The runnable to start camera after the gl surface view is attached.
// For video input source, videoInput.start() will be called when the video uri is available.
if (inputSource == InputSource.CAMERA) {
glSurfaceView.post(this::startCamera);
}
// Updates the preview layout.
FrameLayout frameLayout = findViewById(R.id.preview_display_layout);
imageView.setVisibility(View.GONE);
frameLayout.removeAllViewsInLayout();
frameLayout.addView(glSurfaceView);
glSurfaceView.setVisibility(View.VISIBLE);
frameLayout.requestLayout();
}
private void startCamera() {
cameraInput.start(
this,
hands.getGlContext(),
CameraInput.CameraFacing.FRONT,
glSurfaceView.getWidth(),
glSurfaceView.getHeight());
}
private void stopCurrentPipeline() {
if (cameraInput != null) {
cameraInput.setNewFrameListener(null);
cameraInput.close();
}
if (glSurfaceView != null) {
glSurfaceView.setVisibility(View.GONE);
}
if (hands != null) {
hands.close();
}
}
private void logWristLandmark(HandsResult result, boolean showPixelValues) {
if (result.multiHandLandmarks().isEmpty()) {
return;
}
NormalizedLandmark wristLandmark =
result.multiHandLandmarks().get(0).getLandmarkList().get(HandLandmark.WRIST);
// For Bitmaps, show the pixel values. For texture inputs, show the normalized coordinates.
if (showPixelValues) {
int width = result.inputBitmap().getWidth();
int height = result.inputBitmap().getHeight();
Log.i(
TAG,
String.format(
"MediaPipe Hand wrist coordinates (pixel values): x=%f, y=%f",
wristLandmark.getX() * width, wristLandmark.getY() * height));
} else {
Log.i(
TAG,
String.format(
"MediaPipe Hand wrist normalized coordinates (value range: [0, 1]): x=%f, y=%f",
wristLandmark.getX(), wristLandmark.getY()));
}
if (result.multiHandWorldLandmarks().isEmpty()) {
return;
}
Landmark wristWorldLandmark =
result.multiHandWorldLandmarks().get(0).getLandmarkList().get(HandLandmark.WRIST);
Log.i(
TAG,
String.format(
"MediaPipe Hand wrist world coordinates (in meters with the origin at the hand's"
+ " approximate geometric center): x=%f m, y=%f m, z=%f m",
wristWorldLandmark.getX(), wristWorldLandmark.getY(), wristWorldLandmark.getZ()));
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="#+id/buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="?android:attr/buttonBarStyle" android:gravity="center"
android:orientation="horizontal">
<Button
android:id="#+id/button_load_picture"
android:layout_width="wrap_content"
style="?android:attr/buttonBarButtonStyle" android:layout_height="wrap_content"
android:text="#string/load_picture" />
<Button
android:id="#+id/button_start_camera"
android:layout_width="wrap_content"
style="?android:attr/buttonBarButtonStyle" android:layout_height="wrap_content"
android:text="#string/start_camera" />
</LinearLayout>
<FrameLayout
android:id="#+id/preview_display_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
I expect how to rotate camera input in 'hands' of 'mediapipe-solutions-examples'
Related
I need to crop the image in elliptical shape, but I do not want to use Ellipse and fill with ImageBrush as mentioned in this link , instead I need the bitmap itself to be in rounded / elliptical instead of rectangular.
Sometimes I would like to crop in rectangular and sometimes in elliptical, so I cannot use Ellipse and fill.
Is there any alternative solution to this? It would also be better if I can clip the Image in elliptical format.
But the Clip in Image accepts only RectangleGeometry.
you can create a custom UserControl and in that use win2d CanvasControl to display image where you can draw image in any shape that you want, using "CreateLayer" funtion to mask the drawing image with shape. for example
at xaml:
<UserControl
x:Class="UWPClassLib.MyImageControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UWPClassLib"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:win2d="using:Microsoft.Graphics.Canvas.UI.Xaml"
mc:Ignorable="d">
<win2d:CanvasControl x:Name="w2dCanvas"
Draw="w2dCanvas_Draw" />
</UserControl>
at code behind :
public enum MaskShape
{
rectangle,
circle
}
public sealed partial class MyImageControl : UserControl
{
public WriteableBitmap Bitmap
{
get { return (WriteableBitmap)GetValue(BitmapProperty); }
set { SetValue(BitmapProperty, value); }
}
public static readonly DependencyProperty BitmapProperty = DependencyProperty.Register(nameof(Bitmap), typeof(WriteableBitmap), typeof(MyImageControl), new PropertyMetadata(null, OnBitmapPropertyChanged));
private static void OnBitmapPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var myctrl = (MyImageControl)d;
myctrl.TryCreateResource();
}
public MaskShape Shape
{
get { return (MaskShape)GetValue(ShapeProperty); }
set { SetValue(ShapeProperty, value); }
}
public static readonly DependencyProperty ShapeProperty = DependencyProperty.Register(nameof(Shape), typeof(MaskShape), typeof(MyImageControl), new PropertyMetadata(MaskShape.circle));
private CanvasBitmap Source;
public MyImageControl()
{
this.InitializeComponent();
}
public void Invalidate()
{
w2dCanvas.Invalidate();
}
private void w2dCanvas_Draw(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender, Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args)
{
using (var session = args.DrawingSession)
{
session.Clear(Colors.Transparent);
if (CheckResourceCreated())
{
using (var mask = GetMaskShape())
using (var layer = session.CreateLayer(1.0f, mask))
{
session.DrawImage(Source);
}
// either you can do that or can use
// session.FillGeometry(mask, imagebrush); //and image brush can be made from source e.g.
// imagebrush = new CanvasImageBrush(w2dCanvas, Source);
}
}
}
private CanvasGeometry GetMaskShape()
{
switch (Shape)
{
default:
case MaskShape.circle:
var center = new System.Numerics.Vector2((float)this.ActualWidth / 2, (float)this.ActualHeight / 2);
var radiusX = (float)this.ActualWidth / 2;
var radiusY = (float)this.ActualHeight / 2;
return CanvasGeometry.CreateEllipse(w2dCanvas, center, radiusX, radiusY);
case MaskShape.rectangle:
return CanvasGeometry.CreateRectangle(w2dCanvas, new Rect(0, 0, this.ActualWidth, this.ActualHeight));
}
}
private bool CheckResourceCreated()
{
if (Source == null)
{
TryCreateResource();
}
return (Source != null);
}
private void TryCreateResource()
{
try
{
if (Bitmap == null)
return;
Source = CanvasBitmap.CreateFromBytes(w2dCanvas, Bitmap.PixelBuffer.ToArray(), Bitmap.PixelWidth, Bitmap.PixelHeight, DirectXPixelFormat.B8G8R8A8UIntNormalized);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
}
and now you can use it any where to display image in rectangle or circle. you just have to change MaskShape and call invalidate
plese forgive any error since, i wrote it on the go and haven't test it.
its just for an idea
Good evening. I have read a lot of topics here on stackoverflow or even internet but I can't find the solution to my problem.
I have an interface like this:
When I click on "Load Image A", I can choose the image that I want. Next I want to paint this image under the JLabel "Image A". But it doesn't want to show up.
Here is the code I wrote:
package projet;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class MonPanelImage extends JPanel{
private static final long serialVersionUID = -8267224342030244581L;
private BufferedImage image;
public MonPanelImage(File adresse)
{
try{
image = ImageIO.read(adresse);
}catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponents(g);
System.out.println("paint");
if(image != null){
g.drawImage(image, 20, 20, this);
}
}
}
and here is where I call it:
//panel image. This is my second panel which will be for the images
final JPanel second = new JPanel(new BorderLayout());
//panel button. This is the third panel for the buttons
rows = 0;
cols = 3;
hgap = 5;
vgap = 0;
JPanel third = new JPanel(new GridLayout(rows,cols,hgap,vgap));
//buttons
JButton boutonLoad1 = new JButton("Load image A");
boutonLoad1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int retour = fc.showDialog(frame, "Charger l'image");
if(retour == JFileChooser.APPROVE_OPTION){
String pathImage1 = fc.getSelectedFile().getAbsolutePath();
path1 = pathImage1;
File file = fc.getSelectedFile();
MonPanelImage panelImage1 = new MonPanelImage(file);
second.add(panelImage1, BorderLayout.WEST);
second.revalidate();
second.repaint();
}
}
});
At the very end, i add the 3 panels to my frame and set the frame to visible.
But I can't paint an image. Maybe I'm not doing it properly. Can someone help me please?
Thanks
super.paintComponents(g);
First of all it should be super.paintComponent(g), without the "s".
second.add(panelImage1, BorderLayout.WEST);
You are adding your image to a component using a BorderLayout. The BorderLayout will respect the width of your component, which is 0, so there is nothing to paint.
Whenever, you do custom painting you need to override the getPreferredSize() method to return the size of your component so the layout manager can do its job.
However, an easier solution is to just use a JLabel with an Icon. There is no need to do custom painting when you are painting the image at its real size.
If I create a ListView in JavaFX like this:
ObservableList<String> elements = FXCollections.observableArrayList("John", "Doe");
ListView<String> lView = new ListView<String>(elements);
What I want to do is draw a line starting from the end of a row in the ListView, say from "John"
To do this, I need the location(x,y) of the row "John". Is it possible to get the location?
Update
This is a sample interface that I got using Swing and Piccolo2D. However, using that library is painful. I am wondering if I can do the same in JavaFX
It is possible, but it may not be as straight forward as you hoped. In order to determine the layout coordinates for a particular Cell within a ListView (or TableView/TreeView) you need to have access to that particular Cell object. The best way (and maybe only way in JavaFX 2.2) is to provide the container with a custom Cell and CellFactory that exposes each Cell. How you expose the Cell depends on what your triggers are for drawing the line.
Bases on your illustration, you'll need access to each cell once the ListViews are populated. You can do this with a List<ListCell<String>> field in the CellFactory. I'll mention one caveat here about ListCells. The ListViewSkin will reuse Cells whenever possible. That means that if you are going to try to populate and connect a list that ends up scrolling, then keeping your lines in the right place will be much more difficult. I'd recommend trying to ensure that all your list items fit on screen.
Below is an example with some notes in the comments. Take note that getting the correct coordinates for drawing your Line will probably require calculating the offset of your SceneGraph which I didn't do in this example.
package listviewcellposition;
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
import javafx.util.Callback;
public class ListViewCellPosition extends Application {
// CustomCellFactory for creating CustomCells
public class CustomCellFactory implements
Callback<ListView<String>, ListCell<String>> {
List<ListCell<String>> allCells = new ArrayList<>();
#Override
public ListCell<String> call(final ListView<String> p) {
final CustomCell cell = new CustomCell();
allCells.add(cell);
return cell;
}
public List<ListCell<String>> getAllCells() {
return allCells;
}
}
// CustomCell is where the exposure occurs. Here, it's based on the
// Cell being selected in the ListView. You could choose a different
// trigger here but you'll need to explore.
public class CustomCell extends ListCell<String> {
// General display stuff
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
setText(item == null ? "" : item);
setGraphic(null);
}
}
}
#Override
public void start(Stage primaryStage) {
// This pane will contain the lines after they are created.
// I set it into an AnchorPane to avoid having to deal with
// resizing.
Pane linePane = new Pane();
AnchorPane pane = new AnchorPane();
pane.setPrefSize(100, 250);
AnchorPane.setBottomAnchor(linePane, 0.0);
AnchorPane.setLeftAnchor(linePane, 0.0);
AnchorPane.setRightAnchor(linePane, 0.0);
AnchorPane.setTopAnchor(linePane, 0.0);
pane.getChildren().add(linePane);
ListView<String> lView = new ListView<>();
lView.setPrefSize(100, 250);
CustomCellFactory lCellFactory = new CustomCellFactory();
lView.setCellFactory(lCellFactory);
ListView<String> rView = new ListView<>();
rView.setPrefSize(100, 250);
CustomCellFactory rCellFactory = new CustomCellFactory();
rView.setCellFactory(rCellFactory);
lView.getItems().addAll("Bill", "Doctor", "Steve", "Joanne");
rView.getItems().addAll("Seuss", "Rowling", "King", "Shakespeare");
HBox root = new HBox();
root.getChildren().addAll(lView, pane, rView);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
connectCells(lCellFactory, "Bill", rCellFactory, "Shakespeare", linePane);
connectCells(lCellFactory, "Doctor", rCellFactory, "Seuss", linePane);
connectCells(lCellFactory, "Steve", rCellFactory, "King", linePane);
connectCells(lCellFactory, "Joanne", rCellFactory, "Rowling", linePane);
}
// Looks up the ListCell<> for each String and creates a Line
// with the coordinates from each Cell. The calculation is very
// contrived because I know that all the components have the same
// x-coordinate. You'll need more complicated calculations if your
// containers are not aligned this way.
private void connectCells(CustomCellFactory lCellFactory, String lVal,
CustomCellFactory rCellFactory, String rVal, Pane linePane) {
List<ListCell<String>> lList = lCellFactory.getAllCells();
ListCell<String> lCell = null;
for (ListCell<String> lc : lList) {
if (lc.getItem() != null && lc.getItem().equals(lVal)) {
lCell = lc;
break;
}
}
List<ListCell<String>> rList = rCellFactory.getAllCells();
ListCell<String> rCell = null;
for (ListCell<String> rc : rList) {
if (rc.getItem() != null && rc.getItem().equals(rVal)) {
rCell = rc;
break;
}
}
if (lCell != null && rCell != null) {
double startY = lCell.getLayoutY() +
(lCell.getBoundsInLocal().getHeight() / 2);
double endY = rCell.getLayoutY() +
(rCell.getBoundsInLocal().getHeight() / 2);
Line line = new Line(0, startY,
linePane.getBoundsInParent().getWidth(), endY);
line.setStrokeWidth(2);
line.setStroke(Color.BLACK);
linePane.getChildren().add(line);
}
}
public static void main(String[] args) {
launch(args);
}
}
I have a sprite animation in my app and it's working fine for standard phone sized screens but whenever I put it on a tablet the animation gets glitchy and doesn't look right at all. I think it has something to do with the height and widths of the image. I've tried setting them as dp and sp but they still won't render right on the tablet.
Here's my animation xml
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:paddingLeft="100dp" >
<view
android:id="#+id/animation"
android:layout_width="180dp"
android:layout_height="220dp"
class="com.scale.AndroidAnimation" />
</LinearLayout>
Here's my animation code
public class AndroidAnimation extends View{
private Bitmap mAnimation;
private Rect mSRectangle;
private int mNoOfFrames;
private int mCurrentFrame;
private int mSpriteHeight;
private int mSpriteWidth;
public AndroidAnimation(Context context, AttributeSet aSet) {
super(context, aSet);
mSRectangle = new Rect(0,0,0,0);
mCurrentFrame = 0;
}
public void init(Bitmap theBitmap, int Height, int Width, int theFrameCount) {
mAnimation = theBitmap;
mSpriteHeight = Height;
mSpriteWidth = Width;
mSRectangle.top = 0;
mSRectangle.bottom = mSpriteHeight;
mSRectangle.left = 0;
mSRectangle.right = mSpriteWidth;
mNoOfFrames = theFrameCount;
mCurrentFrame = 0;
}
public void update() {
mCurrentFrame++;
mCurrentFrame %= mNoOfFrames;
mSRectangle.left = mCurrentFrame * mSpriteWidth;
mSRectangle.right = mSRectangle.left + mSpriteWidth;
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Rect dest = new Rect(0, 0, mSpriteWidth, mSpriteHeight);
canvas.drawBitmap(mAnimation, mSRectangle, dest, null);
}
}
Where I initialize it
anim = (AndroidAnimation) findViewById(R.id.animation);
anim.init(BitmapFactory.decodeResource(getResources(), R.drawable.connecting_sprite), 300, 170, 3);
mHandler.removeCallbacks(myTimerTask);
mHandler.postDelayed(myTimerTask, 300);
My handler that progresses it
public void run() {
mHandler.removeCallbacks(myTimerTask);
if (mCurrScreen == WAITING_SCREEN) {
mHandler.postDelayed(myTimerTask, 300);
}
anim.update();
anim.invalidate();
}
Thanks in advance!
It is good way to design seperate layout for tablet.Create layout-xlarge folder in res and put your new layout animation.xml file which is optimized for tablet screen.
finally I got the idea how to handle touch and gestures events on Blackberry, but now I have 2 questions:
1)how to render graphics above another graphis while being able to handle touch events?
2)how to draw simple rectangle on touch event, say CLICK?
My code & screen:
package mypackage;
import net.rim.device.api.lcdui.game.BlackBerryGameCanvas;
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.TouchGesture;
import net.rim.device.api.ui.VirtualKeyboard;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.component.Menu;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.container.VerticalFieldManager;
import net.rim.device.api.ui.TouchEvent;
public class MyScreen extends MainScreen
{
LabelField touch_type;
public boolean onMenu(int instance) {
return instance == Menu.INSTANCE_CONTEXT ? false : super.onMenu(instance);
}
public MyScreen()
{
super(NO_SYSTEM_MENU_ITEMS);
getScreen().getVirtualKeyboard().setVisibility(VirtualKeyboard.HIDE_FORCE);
VerticalFieldManager vf = new VerticalFieldManager();
touch_type = new LabelField("SOME TEXT", FIELD_HCENTER);
vf.add(touch_type);
vf.add(new HandleTouch());
add(vf);
}
/*
* Implementing touch handler class
*/
class HandleTouch extends Field {
protected void layout(int width, int height) {
setExtent(360, 460);
}
public void paint(Graphics graphics) {
graphics.drawBitmap(0, 0, this.getWidth(), this.getHeight(), Bitmap.getBitmapResource("bg.png"), 0, 0);
}
protected void drawFocus(Graphics g, boolean on) {}
public boolean isFocusable() { return true;}
public void drawBall(int x, int y) {
}
protected boolean touchEvent(TouchEvent message) {
switch( message.getEvent() ) {
case TouchEvent.CLICK:
int x = message.getGlobalX(1);
int y = message.getGlobalY(1);
touch_type.setText("CLICK");
return true;
case TouchEvent.DOWN:
//System.out.println("----------------------------->DOWN");
touch_type.setText("DOWN");
return true;
case TouchEvent.MOVE:
//System.out.println("----------------------------->MOVE");
touch_type.setText("MOVE");
return true;
case TouchEvent.UNCLICK:
//System.out.println("----------------------------->UNCLICK");
touch_type.setText("UNCLICK");
return true;
case TouchEvent.GESTURE:
TouchGesture gesture = message.getGesture();
int gestureCode = gesture.getEvent();
switch (gestureCode) {
case TouchGesture.HOVER:
//System.out.println("----------------------------->HOVER");
touch_type.setText("HOVER");
return true;
case TouchGesture.SWIPE:
//System.out.println("----------------------------->SWIPE");
touch_type.setText("SWIPE");
return true;
case TouchGesture.TAP:
//System.out.println("----------------------------->TAP");
touch_type.setText("TAP");
return true;
case TouchGesture.CLICK_REPEAT:
//System.out.println("----------------------------->CLICK REPEAT");
touch_type.setText("CLICK REPEAT");
return true;
case TouchGesture.DOUBLE_TAP:
//System.out.println("----------------------------->DOUBLE TAP");
touch_type.setText("DOUBLE TAP");
return true;
}
}
//System.out.println("PRINT ME SOMETHING IN ANY CASE");
super.touchEvent(message);
return false;
}
public HandleTouch() {
}
}
}
1.) I'm not sure what the question actually is. Can you rephrase it or give an example? I'll update this answer once there's something I can figure out.
2.) Just set a flag on the DOWN event, save the touch coordinates, call invalidate(), and in your paint method check for said flag and if it exists draw the rectangle at/around your coordinates. On the UP event unset the flag, call invalidate(), and you will remove the rectangle.