I am trying to build an Android app that simply uses the camera to take a picture without launching the default camera app. In other words, I want to make a custom camera app. I can do this using the Camera hardware object class, however this is deprecated and I want to use some of the new features of camerax and not have to worry about the code not working after some time. I have also read the camera API documentation, however it is still unclear how to use the camera. Are there any very simple step by step tutorials or guides that might help me?
Thanks,
You can check my example about how to use AndroidX libraries and TextureView for camera customization.
https://github.com/icerrate/Custom-Camera-App
First at all, define your layout. This is my activity_main.xml file:
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextureView
android:id="#+id/view_finder"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/take_photo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_margin="#dimen/horizontal_margin"
app:layout_constraintStart_toStartOf="parent"
android:src="#drawable/ic_camera"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Remember that TextureView will receive the camera preview and the Floating Action Button works as a "Take Photo" button.
Then add your MainActivity.java file:
public class MainActivity extends AppCompatActivity implements LifecycleOwner {
private static final int RC_PERMISSIONS = 100;
private TextureView viewFinder;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
viewFinder = findViewById(R.id.view_finder);
FloatingActionButton takePhotoFab = findViewById(R.id.take_photo);
//Check permissions
if (allPermissionGranted()) {
viewFinder.post(new Runnable() {
#Override
public void run() {
startCamera();
}
});
} else {
ActivityCompat.requestPermissions(this,
new String[] {Manifest.permission.CAMERA}, RC_PERMISSIONS);
}
takePhotoFab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
takePhoto();
}
});
}
private void startCamera() {
Point screenSize = getScreenSize();
int width = screenSize.x;
int height = screenSize.y;
//Get real aspect ratio
DisplayMetrics displayMetrics = new DisplayMetrics();
Display display = getWindowManager().getDefaultDisplay();
display.getRealMetrics(displayMetrics);
Rational rational = new Rational(displayMetrics.widthPixels, displayMetrics.heightPixels);
//Build the camera preview
PreviewConfig build = new PreviewConfig.Builder()
.setTargetAspectRatio(rational)
.setTargetResolution(new Size(width,height))
.build();
Preview preview = new Preview(build);
preview.setOnPreviewOutputUpdateListener(new Preview.OnPreviewOutputUpdateListener() {
#Override
public void onUpdated(Preview.PreviewOutput output) {
ViewGroup group = (ViewGroup) viewFinder.getParent();
group.removeView(viewFinder);
group.addView(viewFinder, 0);
viewFinder.setSurfaceTexture(output.getSurfaceTexture());
}
});
CameraX.bindToLifecycle(this, preview);
}
private void takePhoto() {
Toast.makeText(this, "Shot!",
Toast.LENGTH_SHORT).show();
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == RC_PERMISSIONS) {
if (allPermissionGranted()) {
viewFinder.post(new Runnable() {
#Override
public void run() {
startCamera();
}
});
} else {
Toast.makeText(this, "Permission not granted",
Toast.LENGTH_SHORT).show();
finish();
}
}
}
private boolean allPermissionGranted() {
return ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;
}
private Point getScreenSize() {
Display display = getWindowManager(). getDefaultDisplay();
Point size = new Point();
display.getSize(size);
return size;
}
}
In this class, you would be able to send the camera preview to the TextureView, with help of PreviewConfig.Builder() and binding it to the Activity lifeCycle using CameraX.bindToLifeCycle()
Also, don't forget to add Camera permission to the manifest and consider runtime permissions.
Screenshot:
Custom Camera preview
Hope this help you!
Related
I have a fragment to display a queue of either videos of images. The video I display in the VideoView works fine, it replays, it's golden. But the images I put in the ImageView just appear invisible. I tried loading them through Uri, by reading a Bitmap, now they're set up with Picasso, and none of it fixed it. The AssetObtainer you'll see in MultimediaPlayer works with both sound files and videos so far, so I highly doubt it has an issue with images. Here's the code:
MultimediaPlayer.java :
public class MultimediaPlayer extends Fragment
{
VideoView mVideoView;
ImageView mImageView;
MultimediaViewModel mMultimediaViewModel;
Play mPlayThread;
Activity mActivity;
AssetObtainer assetObtainer = new AssetObtainer();
public Long mTutorialId;
public List<Multimedia> multimedias = new LinkedList<>();
#Override
public View onCreateView(#NotNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
return inflater.inflate(R.layout.fragment_multimedia_player, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
mActivity = requireActivity();
mMultimediaViewModel = new ViewModelProvider(this).get(MultimediaViewModel.class);
mVideoView = view.findViewById(R.id.video_embed);
mImageView = view.findViewById(R.id.image_embed);
getPlayer(0);
}
private void getPlayer(int position)
{
if(mPlayThread != null) {
mPlayThread.interrupt();
position++;
}
if(!multimedias.isEmpty()) {
mPlayThread = new Play(multimedias.get(position));
mPlayThread.start();
}
}
private class Play extends Thread
{
private final Multimedia currentMedia;
Play(Multimedia media){
currentMedia = media;
}
#Override
public void run()
{
int position = currentMedia.getPosition();
int displayTime = currentMedia.getDisplayTime();
boolean loopBool = currentMedia.getLoop();
if(currentMedia.getType()) {
mActivity.runOnUiThread(() -> {
mImageView.setVisibility(View.VISIBLE);
mVideoView.setVisibility(View.GONE);
try {
Picasso.get().load(assetObtainer.getFileFromAssets(requireContext(), currentMedia.getFullFileName())).into(mImageView);
} catch (IOException ignored) {}
});
if(displayTime>0) {
try {
sleep(displayTime);
if(!loopBool) multimedias.remove(currentMedia);
if(position<multimedias.size()-1) {
getPlayer(position);
} else getPlayer(0);
} catch (InterruptedException e) {
mActivity.runOnUiThread(() -> mImageView.setVisibility(View.GONE));
interrupt();
}
}
} else {
mActivity.runOnUiThread(() -> {
mVideoView.setVisibility(View.VISIBLE);
mImageView.setVisibility(View.GONE);
try {
mVideoView.setVideoURI(Uri.fromFile(assetObtainer.getFileFromAssets(requireContext(), currentMedia.getFullFileName())));
} catch (IOException ignored) {}
if(loopBool && multimedias.size()==1) mVideoView.setOnCompletionListener(v->getPlayer(position-1));
mVideoView.start();
});
}
}
}
#Override
public void onPause()
{
if(mPlayThread!=null){
mPlayThread.interrupt();
}
super.onPause();
}
}
bed for the fragment in the activity .xml file :
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/layout_multimedia"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/active_instructions" />
and the .xml file of the fragment :
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".tutorial.mediaplayer.MultimediaPlayer">
<VideoView
android:id="#+id/video_embed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="#+id/image_embed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
android:contentDescription="#string/image_default_no_description" />
</androidx.constraintlayout.widget.ConstraintLayout>
As mentioned, video works super fine, but images won't show. I'd appreciate even monkey wrench suggestions before I have to rework this entirely.
As mentioned in my other answer, it's not related to the ImageView, it's a problem with SQLite and loading a boolean from the database. The default false which I wanted to correspond with video types is default, so it worked, but since it doesn't default to true the image condition wasn't fulfilled. Anyway the ImageView will work once it is actually ran.
I am new to coding and at this point, I made a media player that starts and pauses the audio, has a working seekbar, duration, etc. Now I faced a big issue. The audio can't be played in the background and I found out that I can do this with service but this changes things. I read all kinds of topics on how to control the audio in service with seekbar and all kinds of stuff but nothing helped me. The main problem I face is having the seekbar to control the audio and a text to read the time. If there is someone to help me find a code for this it would be much appreciated.
The layout:
<SeekBar
android:id="#+id/seekBarMusic1"
android:layout_width="match_parent"
android:layout_height="#dimen/_20sdp"
android:layout_marginTop="#dimen/_12sdp"
android:layout_marginLeft="#dimen/_20sdp"
android:layout_marginRight="#dimen/_20sdp"
app:layout_constraintTop_toBottomOf="#+id/cardView"
tools:layout_editor_absoluteX="10dp" />
<TextView
android:id="#+id/playerPositionMusic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00:00"
android:textColor="#color/remain_black"
android:textSize="25dp"
android:layout_marginBottom="#dimen/_80sdp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="#+id/cardView"
app:layout_constraintTop_toBottomOf="#+id/seekBarMusic1" />
<TextView
android:id="#+id/text_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="#dimen/_80sdp"
android:text="15:00"
android:textColor="#color/remain_black"
android:textSize="25dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="#+id/cardView"
app:layout_constraintTop_toBottomOf="#+id/seekBarMusic1" />
<ImageView
android:id="#+id/btPlayMusic"
android:layout_width="80dp"
android:layout_height="80dp"
android:alpha="0.9"
android:background="#drawable/play"
android:theme="#style/Button.White"
app:layout_constraintEnd_toStartOf="#+id/text_time"
app:layout_constraintStart_toEndOf="#+id/playerPositionMusic"
app:layout_constraintTop_toBottomOf="#+id/seekBarMusic1"
app:tint="#color/remain_black" />
<ImageView
android:visibility="gone"
android:id="#+id/btPauseMusic"
android:layout_width="80dp"
android:layout_height="80dp"
android:alpha="0.9"
android:background="#drawable/pause"
android:theme="#style/Button.White"
app:layout_constraintEnd_toStartOf="#+id/text_time"
app:layout_constraintStart_toEndOf="#+id/playerPositionMusic"
app:layout_constraintTop_toBottomOf="#+id/seekBarMusic1"
app:tint="#color/remain_black" />
The code I used before:
public class m1 extends AppCompatActivity {
ImageView btPlay, btPause;
TextView playerPosition, playerDuration;
CircularSeekBar seekBar;
MediaPlayer mediaPlayer;
Handler handler = new Handler();
Runnable runnable;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_m1);
findViewById(R.id.backm1).setOnClickListener(v -> onBackPressed());
playerPosition = findViewById(R.id.playerPosition);
playerDuration = findViewById(R.id.playerDuration);
replay = findViewById(R.id.replay);
forward = findViewById(R.id.forward);
seekBar = findViewById(R.id.seekBar);
btPause = findViewById(R.id.btPause);
btPlay = findViewById(R.id.btPlay);
mediaPlayer = MediaPlayer.create(this,R.raw.ding_dong);
runnable = new Runnable() {
#Override
public void run() {
seekBar.setProgress(mediaPlayer.getCurrentPosition());
handler.postDelayed(this,500);
}
};
int duration = mediaPlayer.getDuration();
String sDuration = convertFormat(duration);
playerDuration.setText(sDuration);
btPlay.setOnClickListener(v -> {
mediaPlayer.start();
btPlay.setVisibility(View.GONE);
btPause.setVisibility(View.VISIBLE);
seekBar.setMax(mediaPlayer.getDuration());
handler.postDelayed(runnable, 0)
});
btPause.setOnClickListener(v -> {
mediaPlayer.stop();
btPause.setVisibility(View.GONE);
btPlay.setVisibility(View.VISIBLE);
handler.removeCallbacks(runnable);
});
seekBar.setOnSeekBarChangeListener(new CircularSeekBar.OnCircularSeekBarChangeListener() {
#Override
public void onProgressChanged(CircularSeekBar circularSeekBar, float v, boolean b) {
if (b) {
mediaPlayer.seekTo((int) v);
}
playerPosition.setText(convertFormat(mediaPlayer.getCurrentPosition()));
}
#Override
public void onStopTrackingTouch(CircularSeekBar circularSeekBar) {
}
#Override
public void onStartTrackingTouch(CircularSeekBar circularSeekBar) {
}
});
mediaPlayer.setOnCompletionListener(mp -> {
btPause.setVisibility((View.GONE));
btPlay.setVisibility(View.VISIBLE);
mediaPlayer.seekTo(0);
});
}
}
#SuppressLint("DefaultLocale")
private String convertFormat(int duration) {
return String.format("%02d:%02d"
,TimeUnit.MILLISECONDS.toMinutes(duration)
,TimeUnit.MILLISECONDS.toSeconds(duration) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(duration)));
}
#Override
public void onPause()
{
if(mediaPlayer != null && mediaPlayer.isPlaying())
{
mediaPlayer.stop();
}
super.onPause();
}
}
The code I am using now:
btPause = findViewById(R.id.btPauseMusic);
btPlay = findViewById(R.id.btPlayMusic);
btPlay.setOnClickListener(v -> {
startService(new Intent(this, BackgroundMusicService.class));
btPlay.setVisibility(View.GONE);
btPause.setVisibility(View.VISIBLE);
});
btPause.setOnClickListener(v -> {
stopService(new Intent(this, BackgroundMusicService.class));
btPause.setVisibility(View.GONE);
btPlay.setVisibility(View.VISIBLE);
});
}
And the service class:
public class BackgroundMusicService extends Service {
private MediaPlayer mediaPlayer;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
mediaPlayer = MediaPlayer.create(this, R.raw.ding_dong);
mediaPlayer.start();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
mediaPlayer.stop();
}
}
You are using different MediaPlayer in the Activity from the one in the backgroundService
you don't need to initialize another mediaPlayer in the activity you have to control the one in the backgroundService with Broadcast receivers like this for example to pause a the mediaPlayer in the backgroundService you do something like this
private BroadcastReceiver pausePlayingAudio = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
pause();
updateMetaData();
buildNotification(PlaybackStatus.PAUSED);
playbackStatus = PlaybackStatus.PAUSED ;
}
};
private void register_pausePlayingAudio(){
IntentFilter intentFilter = new IntentFilter(SongAdapter.ACTION_PAUSE) ;
registerReceiver(pausePlayingAudio , intentFilter) ;
}
and you call register_pausePlayingAudio() in onCreate() of the backGroundService
now say when a user clicks on a button in the activity and you want to pause you use something like this
sendBroadcast(new Intent(SongAdapter.ACTION_PAUSE));
I have and old project not finished doesn't have a seekBar but the seekBar implementation will be similar just instead of pause() you call seekTo() so you can check it
the repo
This article Creating Media player service shows step by step on how you can implement a service to create a Mediaplayer application to run in the background.
I want to import Google Map SDK in my project. According to Docs I follow all the steps perfectly but when i run the project it only show an icon of google instead of Google Map.
I search a lot but nothing find to solve my problem
Here is my Main Activity named as GoogleMapLocat:
public class GoogleMapLocat extends AppCompatActivity {
//map data
private static final String TAG = "HomeActivity";
private static final int ERROR_DIALOG_REQUEST = 9001;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_google_map_locat);
//map data
if (isServicesOK()) {
init();
}
}
//MAP DATA
public void init() {
Button btnMap = (Button) findViewById(R.id.btnMap);
btnMap.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(GoogleMapLocat.this, MapActivity.class);
startActivity(intent);
}
});
}
public boolean isServicesOK() {
Log.d(TAG, "isServicesOK: checking google services version");
int available = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(GoogleMapLocat.this);
if (available == ConnectionResult.SUCCESS) {
Log.d(TAG, "Google Play Services is Working");
return true;
} else if (GoogleApiAvailability.getInstance().isUserResolvableError(available)) {
//an error occured but we can resolve it
Log.d(TAG, "isServicesOK: an error occured but we can fix it");
Dialog dialog = GoogleApiAvailability.getInstance().getErrorDialog(GoogleMapLocat.this, available, ERROR_DIALOG_REQUEST);
dialog.show();
} else {
Toast.makeText(this, "you can,t make map request", Toast.LENGTH_LONG).show();
}
return false;
}
}
Here is my Second Activity named as MapActivity:
public class MapActivity extends FragmentActivity implements OnMapReadyCallback{
private static final String TAG = "MapActivity";
private static final String FINE_LOCATION = Manifest.permission.ACCESS_FINE_LOCATION;
private static final String COURSE_LOCATION = Manifest.permission.ACCESS_COARSE_LOCATION;
private static final int LOCATION_PERMISSION_REQUEST_CODE = 1234;
//vars
private Boolean mLocationPermissionsGranted = false;
private GoogleMap mMap;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
getLocationPermission();
}
#Override
public void onMapReady(GoogleMap googleMap) {
Toast.makeText(this, "Map is Ready", Toast.LENGTH_SHORT).show();
Log.d(TAG, "onMapReady: map is ready");
mMap = googleMap;
}
private void initMap(){
Log.d(TAG, "initMap: initializing map");
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(MapActivity.this);
}
private void getLocationPermission(){
Log.d(TAG, "getLocationPermission: getting location permissions");
String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION};
if(ContextCompat.checkSelfPermission(this.getApplicationContext(),
FINE_LOCATION) == PackageManager.PERMISSION_GRANTED){
if(ContextCompat.checkSelfPermission(this.getApplicationContext(),
COURSE_LOCATION) == PackageManager.PERMISSION_GRANTED){
mLocationPermissionsGranted = true;
}else{
ActivityCompat.requestPermissions(this,
permissions,
LOCATION_PERMISSION_REQUEST_CODE);
}
}else{
ActivityCompat.requestPermissions(this,
permissions,
LOCATION_PERMISSION_REQUEST_CODE);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
Log.d(TAG, "onRequestPermissionsResult: called.");
mLocationPermissionsGranted = false;
switch(requestCode){
case LOCATION_PERMISSION_REQUEST_CODE:{
if(grantResults.length > 0){
for(int i = 0; i < grantResults.length; i++){
if(grantResults[i] != PackageManager.PERMISSION_GRANTED){
mLocationPermissionsGranted = false;
Log.d(TAG, "onRequestPermissionsResult: permission failed");
return;
}
}
Log.d(TAG, "onRequestPermissionsResult: permission granted");
mLocationPermissionsGranted = true;
//initialize our map
initMap();
}
}
}
}
}
GoogleMapLocat.xml is here:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".GoogleMapLocat">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Map"
android:id="#+id/btnMap"
tools:ignore="MissingConstraints" />
MapActivity.xml is here:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment 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:id="#+id/map"
tools:context=".MapsActivity"
android:name="com.google.android.gms.maps.SupportMapFragment" />
What,s wrong in this code?It only shows google icon in left corner at the bottom of screen instead of map.
I can't see if you have configured the google map key. You have to define it in the manifest. You can define with this line:
<meta-data android:name="com.google.android.geo.API_KEY" android:value="YOUR_KEY_GOES_HERE" />
You can review this link to get your key: https://developers.google.com/maps/documentation/android-sdk/get-api-key
I suggest you must define 2 APIs for release & debug in APIs & credentials.
For example, use debug & release sign key in both APIs.
I had the same problem a few days ago.
Try to remove "Application restrictions" on Google API Console.
It worked for me.
I was testing on device and it was not connected to internet...
I connected to WiFi and map started showing, if was silently failing.
If someone end up on this question, don't forget to check your connection.
I encountered this problem too and I had ensured that:
The project SDK was setup correctly
Google Console was setup correctly with no key restriction
I confirmed the above by using the same key in other android project and the map could load successfully. Then I created another new android project with the same package name with my original one and surprising this time the map could not load again. I concluded it was the package name that made the map loading failed. I am not sure why but I solved this by refactoring my package name to another one.
I have a GIF image in my app and want this to become my full screen background. I use width and height = match parent but it's not becoming full screen. Here is my code :
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_page);
GifView pGif = (GifView) findViewById(R.id.gif);
pGif.setImageResource(R.drawable.bbbb);
}}
and this is my class:
public class GifView extends View {
private static final int DEFAULT_MOVIEW_DURATION = 1000;
private int mMovieResourceId;
private Movie mMovie;
private long mMovieStart = 0;
private int mCurrentAnimationTime = 0;
#SuppressLint("NewApi")
public GifView(Context context, AttributeSet attrs) {
super(context, attrs);
/**
* Starting from HONEYCOMB have to turn off HardWare acceleration to draw
* Movie on Canvas.
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
}
public void setImageResource(int mvId){
this.mMovieResourceId = mvId;
mMovie = Movie.decodeStream(getResources().openRawResource(mMovieResourceId));
requestLayout();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if(mMovie != null){
setMeasuredDimension(mMovie.width(), mMovie.height());
}else{
setMeasuredDimension(getSuggestedMinimumWidth(), getSuggestedMinimumHeight());
}
}
#Override
protected void onDraw(Canvas canvas) {
if (mMovie != null){
updateAnimtionTime();
drawGif(canvas);
invalidate();
}else{
drawGif(canvas);
}
}
private void updateAnimtionTime() {
long now = android.os.SystemClock.uptimeMillis();
if (mMovieStart == 0) {
mMovieStart = now;
}
int dur = mMovie.duration();
if (dur == 0) {
dur = DEFAULT_MOVIEW_DURATION;
}
mCurrentAnimationTime = (int) ((now - mMovieStart) % dur);
}
private void drawGif(Canvas canvas) {
mMovie.setTime(mCurrentAnimationTime);
mMovie.draw(canvas, 0, 0);
canvas.restore();
}
}
and xml :
<com.example.fabulous.comic.GifView
android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/gif"/>
You can do this with help of glide
It is an image loading and caching library for Android focused on smooth scrolling
Glide supports fetching, decoding, and displaying video stills, images, and animated GIFs
you can use Glide using this
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.android.support:support-v4:19.1.0'
xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/relative"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/img"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY" />
<!--Rest of your coding-->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
</FrameLayout>
java
ImageView img=(ImageView)findViewById(R.id.img);
Glide.with(Gif.this).load(R.drawable.giphy).asGif().into(img);
ps: i do not tested the code
Try using WebView, layout your view using simple html with your background as the gif then put it on your assets directory and load it using WebView. I have tried this on my other project, probably not the best solution but it works and quite easy.
In file build.gradle(module) add dependencies:
dependencies {
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.android.support:support-v4:19.1.0'
}
For a simple view:
#Override public void onCreate(Bundle savedInstanceState) {
...
ImageView imageView = (ImageView) findViewById(R.id.my_image_view);
Glide.with(this).load("*link to image*").into(imageView);
}
Set in all layouts around image:
android:layout_width="match_parent"
android:layout_height="match_parent"
I'm trying to recover all touch events that occur anywhere on a mobile device (i.e. not only restricted to one activity)
I have a service:
public class OverlayExampleService extends Service implements View.OnTouchListener {
private String TAG = this.getClass().getSimpleName();
// window manager
private WindowManager mWindowManager;
// linear layout will use to detect touch event
private LinearLayout touchLayout;
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
// create linear layout
touchLayout = new LinearLayout(this);
// set layout width 30 px and height is equal to full screen
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(30, WindowManager.LayoutParams.MATCH_PARENT);
touchLayout.setLayoutParams(lp);
// set color if you want layout visible on screen
// touchLayout.setBackgroundColor(Color.CYAN);
// set on touch listener
touchLayout.setOnTouchListener(this);
// fetch window manager object
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
// set layout parameter of window manager
WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT, // width of layout 30 px
WindowManager.LayoutParams.MATCH_PARENT, // height is equal to full screen
WindowManager.LayoutParams.TYPE_PHONE, // Type Phone, These are non-application windows providing user interaction with the phone (in particular incoming calls).
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, // this window won't ever get key input focus
PixelFormat.TRANSLUCENT);
mParams.gravity = Gravity.LEFT | Gravity.TOP;
Log.e(TAG, "add View");
mWindowManager.addView(touchLayout, mParams);
}
#Override
public void onDestroy() {
if(mWindowManager != null) {
if(touchLayout != null) mWindowManager.removeView(touchLayout);
}
super.onDestroy();
}
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_UP)
Log.e(TAG, "Action :" + event.getAction() + "\t X :" + event.getRawX() + "\t Y :"+ event.getRawY());
return false;
}
}
And an activity that starts it:
public class MainActivity extends Activity {
Intent globalService;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
globalService = new Intent(this, OverlayExampleService.class);
}
public void buttonClicked(View v){
if(v.getTag() == null){
startService(globalService);
v.setTag("on");
Toast.makeText(this, "Start Service", Toast.LENGTH_SHORT).show();
}
else{
stopService(globalService);
v.setTag(null);
Toast.makeText(this, "Stop Service", Toast.LENGTH_SHORT).show();
}
}
}
Here is the activity's layout 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"
tools:context=".MainActivity" >
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:text="Start Touch Detection"
android:onClick="buttonClicked" />
</RelativeLayout>
This works, and I can successfully recover Touch X/Y locations but once that is done the touch event is consumed. I would like it to continue onto the activity/display in the backgorund (i.e. Home screen, other apps etc.).
I have tried returning false in the onTouch function but that does not seem to work.
I also know about this issue: Android 4.2 ACTION_OUTSIDE MotionEvent X and Y return 0 outside of own application and that is why I have my windowManger parameters spanning the whole screen (MATCH_PARENT parameters).
So, is there a way to achieve this?