2

Hi I'm trying to generate gif from animated view

In brief, I've ImageView and animated view inside FrameLayout, now i want to generate gif of main FrameLayout, minimum sdk level 19. getDrawingCache bitmap and arrange them in sequence to gif may cause memory out of bound issue.

enter image description here

when i click "start rec" i want gif or video of that particular view.

Mahendra Gohil
  • 380
  • 1
  • 3
  • 21
  • Could you please share your code as of now so that I can understand it better and answer? – Akshat Tiwari Aug 06 '21 at 15:57
  • I'm not sure I understand what you are trying to achieve. – Cristian Holdunu Aug 06 '21 at 20:15
  • hey @AkshatTiwari please check image attached – Mahendra Gohil Aug 07 '21 at 04:09
  • Hey @CristianHoldunu please check now – Mahendra Gohil Aug 07 '21 at 04:09
  • If I understand your requirements correctly, the answer to this is probably out of scope of a SO question. You'll need a bitmap processing pipeline that can transform the bitmaps into gifs, with your implementation supplying the bitmaps at the rate you desire. Possibly this? https://github.com/nbadal/android-gif-encoder – Luke Duncan Aug 08 '21 at 00:12
  • like @LukeDuncan said, you need to build a pipeline to encode the images into a gif. I think for your usecase, you can use the parent `ViewGroup` to extract the bitmaps using `getDrawingCache` method at different intervals, then feed the bitmaps into a GIF assembly pipeline. – Cristian Holdunu Aug 09 '21 at 11:06
  • @CristianHoldunu i already tried that but it's too slow and cause memory out of bound issue, and cause gif lagging – Mahendra Gohil Aug 09 '21 at 11:24
  • That could be optimised and you don't need to store all the images in memory. If you want a true screen recording, you can try MediaProjection https://developer.android.com/guide/topics/media/av-capture – Cristian Holdunu Aug 09 '21 at 15:07
  • Please check these questions - https://stackoverflow.com/questions/14336338/screen-video-record-of-current-activity-android, https://stackoverflow.com/questions/48401817/record-screen-specific-view-using-mediarecorder – Android Geek Aug 10 '21 at 04:21
  • @MahendraGohil, did you find any solution if no, let me know I can help, I've already made the same thing in one of my apps – Mouaad Abdelghafour AITALI Aug 10 '21 at 13:10
  • hello @MouaadAbdelghafourAITALI still not found any solution – Mahendra Gohil Aug 11 '21 at 03:36
  • @MahendraGohil, I've given my answer, please follow all steps, and you will get what you're looking for – Mouaad Abdelghafour AITALI Aug 11 '21 at 11:28

1 Answers1

0

First, you need to implement these libraries :

implementation 'com.blankj:utilcodex:1.30.6'
implementation 'com.waynejo:androidndkgif:0.3.3'
implementation 'com.jiang.android.observablescheduler:schedule:1.0.1'
  1. The first one is a useful library, which contains all the necessary classes to help you, such as (ImageUtils, PathUtils...).
  2. The second one is for the encoding and decoding GIF.
  3. The Third one is for replacing the AsyncTask class with java.util.concurrent

The XML : I tried to make the same thing you did :

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:gravity="center"
        android:orientation="vertical">

        <RelativeLayout
            android:id="@+id/viewToBeRecorded"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_gravity="center"
            android:background="@android:color/holo_green_light"
            android:gravity="center">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:layout_centerVertical="true"
                android:gravity="center"
                android:text="View To Be Recorded"
                android:textStyle="bold" />
        </RelativeLayout>

        <Button
            android:id="@+id/changeBg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Change Background"
            android:textStyle="bold" />

        <Button
            android:id="@+id/startRecord"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Start Record"
            android:textStyle="bold" />

        <Button
            android:id="@+id/stopRecord"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:enabled="false"
            android:text="Stop Record"
            android:textStyle="bold" />
    </LinearLayout>

Java :

public class MainActivity extends AppCompatActivity {
    
    private Button changeBg, startRecord, stopRecord;
    private RelativeLayout viewToBeRecorded;
    private boolean isRecording = false;
    private GifEncoder mGifEncoder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initializeViews();

        startRecord.setOnClickListener(v -> {
            isRecording = true;
            ToastUtils.showShort("Start recording...");
            startRecord.setEnabled(false);
            stopRecord.setEnabled(true);
            generateGIF();
        });

        stopRecord.setOnClickListener(v -> {
            isRecording = false;
            ToastUtils.showShort("Stop recording...");
            startRecord.setEnabled(true);
            stopRecord.setEnabled(false);
        });

        changeBg.setOnClickListener(v -> {
            Random rnd = new Random();
            int color = Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256));
            viewToBeRecorded.setBackgroundColor(color);
        });

    }

    private void generateGIF() {
        JObservable.create((JObservable.OnSubscribe<String>) mSubscriber -> {
            try {
                File storageDir = new File(PathUtils.getExternalPicturesPath() + "/GIFs");
                FileUtils.createOrExistsDir(storageDir);
                File gifFile = new File(storageDir, "GIF" + System.currentTimeMillis() + ".gif");
                String outputPath = gifFile.getAbsolutePath();
                Bitmap firstFrame = ImageUtils.view2Bitmap(viewToBeRecorded);
                try {
                    mGifEncoder.init(firstFrame.getWidth(), firstFrame.getHeight(), outputPath, GifEncoder.EncodingType.ENCODING_TYPE_SIMPLE_FAST);
                    while (isRecording) {
                        Bitmap frame = ImageUtils.view2Bitmap(viewToBeRecorded);
                        mGifEncoder.encodeFrame(frame, 100);
                        Thread.sleep(20);
                    }
                    mGifEncoder.close();
                    mSubscriber.notifyData(outputPath);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            } catch (Exception e) {
                mSubscriber.error(e);
            }
        }).workedOn(Schedules.background())
                .subscribeOn(Schedules.mainThread())
                .subscribe(new Subscriber<String>() {
                    @Override
                    public void notifyData(String outputPath) {
                        FileUtils.notifySystemToScan(outputPath);
                        ToastUtils.showShort("GIF saved!");
                    }

                    @Override
                    public void error(Throwable t) {
                        Log.e("View2GIF", "generateGIF throw error : " + t.getMessage());
                    }
                });

    }

    private void initializeViews() {
        viewToBeRecorded = findViewById(R.id.viewToBeRecorded);
        changeBg = findViewById(R.id.changeBg);
        stopRecord = findViewById(R.id.stopRecord);
        startRecord = findViewById(R.id.startRecord);
        mGifEncoder = new GifEncoder();
    }

OUTPUT :

enter image description here

Don't forget to add :

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

and make sure the Storage permission is granted