แสดงภาพเคลื่อนไหว GIF


261

ฉันต้องการแสดงภาพเคลื่อนไหว GIF ในแอพพลิเคชั่นของฉัน ตามที่ฉันค้นพบวิธียาก Android ไม่สนับสนุน GIF แบบเคลื่อนไหว

อย่างไรก็ตามสามารถแสดงภาพเคลื่อนไหวโดยใช้AnimationDrawable :

พัฒนา> คู่มือ> รูปภาพและกราฟิก> ภาพรวมของ Drawable

ตัวอย่างใช้ภาพเคลื่อนไหวที่บันทึกเป็นเฟรมในทรัพยากรแอปพลิเคชัน แต่สิ่งที่ฉันต้องการคือการแสดงภาพเคลื่อนไหว gif โดยตรง

แผนของฉันคือการแบ่ง GIF เคลื่อนไหวไปยังเฟรมและเพิ่มแต่ละเฟรมเป็น Drawable to AnimationDrawable

ไม่มีใครรู้วิธีแยกเฟรมจากภาพเคลื่อนไหว GIF และแปลงแต่ละภาพเป็นDrawable ได้หรือไม่


3
คุณหมายถึงใน Android หรือการแยกเฟรมจาก gif ด้วยเครื่องมือภายนอกหรือไม่?
Osmund

มันเป็นข้อผิดพลาดอย่างแน่นอนดูIssueTrackerสำหรับข้อมูลเพิ่มเติม
fbtb

สำหรับผู้ที่มาที่นี่เพียงค้นหาแอพที่สามารถแสดง GIF แบบเคลื่อนไหวได้: quickPic
rubo77


ใช้ปูนเปียกเพื่อแสดง GIF github.com/facebook/frescoฉันคิดว่านี่เป็นทางออกที่ง่าย
kaitian521

คำตอบ:


191

Android สามารถถอดรหัสและแสดง GIF แบบเคลื่อนไหวโดยใช้คลาส android.graphics.Movie

นี้ไม่ได้เป็นเอกสารที่มากเกินไป แต่อยู่ในSDK อ้างอิง ยิ่งไปกว่านั้นมันถูกใช้ในSamples ใน ApiDemos ในตัวอย่างBitmapDecode ที่มีแฟล็กเคลื่อนไหว


9
มันเสถียรแค่ไหน? ฉันใช้งานมันในแอพของฉันบนอุปกรณ์ Ice Cream Sandwich ของฉันมันไม่ทำงานเลยในเครื่องจำลอง 2.3 มันใช้งานได้ แต่เฟรม GIF บางชิ้นเป็นรถบั๊กกี้ (สีผิด) แน่นอนว่าในคอมพิวเตอร์มันใช้งานได้ดี สิ่งที่ช่วยให้?
Benoit Duffez

12
@Bicou สำหรับโพสต์ HC คุณควรsetLayerType(LAYER_TYPE_SOFTWARE)ดู แต่มันก็ยังใช้ได้กับ gif และอุปกรณ์บางอย่างเท่านั้น
Michał K

9
@ MichałKขอบคุณ! Paint p = new Paint(); p.setAntiAlias(true); setLayerType(LAYER_TYPE_SOFTWARE, p);ทำงาน!
Chloe

1
@lubosz: LAYER_TYPE_SOFTWARE น่าจะเพียงพอสำหรับการตั้งค่า
ตัวชี้ Null

2
คลาสของภาพยนตร์นั้นไม่มีจุดประสงค์ในการใช้งาน - มันอ่อนแอและไม่สนับสนุน หากคุณต้องการภาพเคลื่อนไหว Gif ที่เหมาะสมคุณควรนำมาใช้ด้วยตนเอง ฉันทำเพื่อแอพของฉันโดยใช้ NDK
ตัวชี้ Null

60

UPDATE:

ใช้ร่อน:

dependencies {
  implementation 'com.github.bumptech.glide:glide:4.0.0'
}

การใช้งาน:

Glide.with(context).load(GIF_URI).into(new GlideDrawableImageViewTarget(IMAGE_VIEW));

ดูเอกสาร


1
ดูเหมือนว่าฉันจะตกลงตอนนี้เห็นได้ชัดว่าคำตอบไม่สามารถรวมไลบรารีทั้งหมดได้ดังนั้นตัวอย่างการโทรดูเหมือนจะเพียงพอ
gaborous

@gaborous เห็นได้ชัดว่ารหัสไม่ได้รวมอยู่ในช่วงเวลาของการตั้งค่าสถานะ
ม.ค.

1
ฉันไม่แนะนำให้ใช้ห้องสมุดนี้หากคุณใช้ NDK อยู่แล้ว หลังจากเพิ่ม lib นี้เพื่อไล่ระดับโมดูล Unity หยุดทำงาน
RominaV

นี่คือห้องสมุดที่ดีที่สุดที่ฉันได้พบ สิ่งหนึ่งที่ฉันติดอยู่คือการบีบนิ้วเพื่อซูมภาพ gif มีใครรู้บ้าง
viper

28

ใส่ยัง (หลัก / สินทรัพย์ / htmls / name.gif) [ด้วย html นี้ปรับให้มีขนาด]

<html style="margin: 0;">
<body style="margin: 0;">
<img src="name.gif" style="width: 100%; height: 100%" />
</body>
</html>

ประกาศใน Xml ของคุณเช่นนี้ (main / res / layout / name.xml): [คุณกำหนดขนาดเช่น]

<WebView
android:layout_width="70dp"
android:layout_height="70dp"
android:id="@+id/webView"
android:layout_gravity="center_horizontal" />

ในกิจกรรมของคุณให้ใส่รหัสถัดไปใน onCreate

web = (WebView) findViewById(R.id.webView); 
web.setBackgroundColor(Color.TRANSPARENT); //for gif without background
web.loadUrl("file:///android_asset/htmls/name.html");

หากคุณต้องการโหลดแบบไดนามิกคุณต้องโหลดเว็บวิวด้วยข้อมูล:

// or "[path]/name.gif" (e.g: file:///android_asset/name.gif for resources in asset folder), and in loadDataWithBaseURL(), you don't need to set base URL, on the other hand, it's similar to loadData() method.
String gifName = "name.gif";
String yourData = "<html style=\"margin: 0;\">\n" +
        "    <body style=\"margin: 0;\">\n" +
        "    <img src=" + gifName + " style=\"width: 100%; height: 100%\" />\n" +
        "    </body>\n" +
        "    </html>";
// Important to add this attribute to webView to get resource from outside.
webView.getSettings().setAllowFileAccess(true);

// Notice: should use loadDataWithBaseURL. BaseUrl could be the base url such as the path to asset folder, or SDCard or any other path, where your images or the other media resides related to your html
webView.loadDataWithBaseURL("file:///android_asset/", yourData, "text/html", "utf-8", null);
// Or if you want to load image from SD card or where else, here is the idea.
String base = Environment.getExternalStorageDirectory().getAbsolutePath().toString();
webView.loadDataWithBaseURL(base + '/', yourData, "text/html", "utf-8", null);

คำแนะนำ: โหลด gif ที่ดีกว่าพร้อมรูปภาพคงที่เพื่อตรวจสอบข้อมูลเพิ่มเติมhttps://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html

แค่นั้นฉันหวังว่าคุณจะช่วย


2
ถ้าฉันต้องการโหลด GIF จาก sdcard?
Jaydev

ถ้าคุณต้องการที่จะโหลดจาก sdcard: แทนที่ "ไฟล์: ///android_asset/htmls/name.html" สำหรับ uri ของภาพของคุณ
Alex Zaraos

เมื่อใช้วิธีนี้ฉันจะเปลี่ยนชื่อไฟล์ gif แบบไดนามิกได้อย่างไร ฉันไม่ต้องการให้ไฟล์ html ไปกับ gif ทุกอันที่ฉันต้องแสดงในแอพของฉัน
scrayne

Webview สำหรับ GIF?!? คุณคิดเกี่ยวกับประสิทธิภาพการใช้หน่วยความจำและแบตเตอรี่หรือไม่
Duna

@Duna เมื่อ 5 ปีที่แล้วปัจจุบันเราสามารถใช้เครื่องร่อนได้
Alex Zaraos

22

ฉันแก้ไขปัญหาโดยการแยกgifภาพเคลื่อนไหวเป็นเฟรมก่อนบันทึกลงในโทรศัพท์ดังนั้นฉันจะไม่ต้องจัดการกับมันใน Android

จากนั้นฉันดาวน์โหลดทุกเฟรมลงในโทรศัพท์สร้าง Drawable จากมันแล้วสร้างAnimationDrawable - คล้ายกับตัวอย่างจากคำถามของฉัน


2
ซึ่งเป็นเอกสารวิธีการจัดการภาพเคลื่อนไหว
RichieHH

16

ฉันพบวิธีที่ง่ายมากพร้อมตัวอย่างการทำงานที่ดีและเรียบง่ายที่นี่

แสดงวิดเจ็ตเคลื่อนไหว

ก่อนที่จะทำให้มันทำงานมีบางสิ่งที่ต้องทำในโค้ด

ในการติดตาม

    @Override
    public void onCreate(Bundle savedInstanceState){    
        super.onCreate(savedInstanceStated);   
        setContentView(new MYGIFView());
    }    
}

เพียงแค่แทนที่

setContentView(new MYGIFView());

ใน

setContentView(new MYGIFView(this));

และใน

public GIFView(Context context) {
    super(context);

ระบุไฟล์ภาพเคลื่อนไหว gif ของคุณเอง

    is = context.getResources().openRawResource(R.drawable.earth);
    movie = Movie.decodeStream(is);
}

แทนที่สายแรกเข้า

public MYGIFView(Context context) {

ตามชื่อของคลาส ...

หลังจากเสร็จสิ้นการเปลี่ยนแปลงเล็ก ๆ น้อย ๆ นี้มันควรจะทำงานสำหรับฉัน ...

หวังว่าความช่วยเหลือนี้


2
คุณลองใช้ Android รุ่นไหน ฉันลองบน Android เวอร์ชั่น 2.2 และ 2.3 แต่มันคืนค่า Object object เป็นโมฆะ มีความคิดในสิ่งที่ผิดหรือเปล่า?
Ashwini Shahapurkar

16
โปรดล้างคำตอบนี้มันสุ่ม
taxeeta

2
ปัญหากับคำตอบคืออะไร? ฉันเพิ่งให้ลิงก์และมีการแก้ไขที่จำเป็นบางอย่างเพื่อให้ทำงานได้
Apperside

10

วิธีแสดง GIF แบบเคลื่อนไหวบน Android:

  • คลาสภาพยนตร์ ดังกล่าวข้างต้นก็ค่อนข้างบั๊ก
  • WebView มันง่ายมากที่จะใช้และมักจะงานได้ แต่บางครั้งมันเริ่มทำงานผิดปกติและมันก็เป็นอุปกรณ์ที่ไม่ชัดเจนที่คุณไม่มีอยู่เสมอ นอกจากนี้คุณไม่สามารถใช้หลายอินสแตนซ์ในมุมมองรายการใด ๆ ได้เพราะมันทำสิ่งต่าง ๆ ในหน่วยความจำ ถึงกระนั้นคุณอาจคิดว่ามันเป็นแนวทางหลัก
  • รหัสที่กำหนดเองเพื่อถอดรหัส gifs เป็นบิตแมปและแสดงเป็น Drawable หรือ ImageView ฉันจะพูดถึงห้องสมุดสองแห่ง:

https://github.com/koral--/android-gif-drawable - ตัวถอดรหัสถูกใช้งานใน C ดังนั้นจึงมีประสิทธิภาพมาก

https://code.google.com/p/giffiledecoder - ตัวถอดรหัสถูกใช้งานใน Java ดังนั้นจึงสามารถใช้งานได้ง่ายขึ้น ยังคงมีประสิทธิภาพพอสมควรแม้จะมีไฟล์ขนาดใหญ่

นอกจากนี้คุณยังจะได้พบกับห้องสมุดจำนวนมากที่อิงกับคลาส GifDecoder นั่นเป็นตัวถอดรหัสที่ใช้ Java แต่ทำงานได้โดยการโหลดไฟล์ทั้งหมดลงในหน่วยความจำดังนั้นจึงสามารถใช้ได้กับไฟล์ขนาดเล็กเท่านั้น


10

ฉันมีช่วงเวลาที่ยากลำบากจริงๆที่จะให้ GIF แบบเคลื่อนไหวทำงานใน Android ฉันทำงานสองอย่างต่อไปนี้เท่านั้น:

  1. WebView
  2. ไอออน

WebView ใช้งานได้ดีและใช้งานง่าย แต่ปัญหาคือมันทำให้มุมมองโหลดช้าลงและแอพจะไม่ตอบสนองต่อวินาที ฉันไม่ชอบมัน ดังนั้นฉันได้ลองแนวทางที่แตกต่างกัน (ไม่ทำงาน):

  1. ImageViewExเลิกใช้แล้ว!
  2. picassoไม่ได้โหลด gif แบบเคลื่อนไหว
  3. android-gif-drawableดูดี แต่มันทำให้เกิดปัญหา NDK แบบใช้สายในโครงการของฉัน มันทำให้ห้องสมุด NDK ในพื้นที่ของฉันหยุดทำงานและฉันไม่สามารถแก้ไขได้

ฉันมีบางอย่างกลับไปกลับมาด้วยIon; ในที่สุดฉันก็ใช้งานได้และมันเร็วมาก :-)

Ion.with(imgView)
  .error(R.drawable.default_image)
  .animateGif(AnimateGifMode.ANIMATE)
  .load("file:///android_asset/animated.gif");

ฉันมีปัญหาที่เกี่ยวข้องกับ NDK เดียวกันกับ android-gif-drawable
RominaV

สวัสดี @RominaV คุณเคยใช้แถบความคืบหน้าในไอออนเมื่อโหลด gif เพราะฉันต้องแสดงความคืบหน้าเมื่อโหลด gif
patel135

9

ร่อน 4.6

1. การโหลด gif

GlideApp.with(context)
            .load(R.raw.gif) // or url
            .into(imageview);

2. เพื่อรับวัตถุไฟล์

GlideApp.with(context)
                .asGif()
                .load(R.raw.gif) //or url
                .into(new SimpleTarget<GifDrawable>() {
                    @Override
                    public void onResourceReady(@NonNull GifDrawable resource, @Nullable Transition<? super GifDrawable> transition) {

                        resource.start();
                      //resource.setLoopCount(1);
                        imageView.setImageDrawable(resource);
                    }
                });

8

Glide

Image Loader Library สำหรับ Android แนะนำโดย Google

  • Glide ค่อนข้างคล้ายกับ Picasso แต่เร็วกว่า Picasso มาก
  • ร่อนใช้หน่วยความจำน้อยกว่า Picasso

สิ่งที่ Glide มี แต่ Picasso ไม่มี

ความสามารถในการโหลดแอนิเมชัน GIF ไปยัง ImageView อย่างง่ายอาจเป็นคุณสมบัติที่น่าสนใจที่สุดของ Glide และใช่คุณไม่สามารถทำได้ด้วย Picasso ลิงค์สำคัญบางอย่าง -ป้อนคำอธิบายรูปภาพที่นี่

  1. https://github.com/bumptech/glide
  2. http://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en

Google แนะนำ Glid ที่ไหน
Derzu

1
ดูสิ่งนี้ที่ทีมพัฒนา Android ใช้ Glide ในแอปตัวอย่างของพวกเขา developer.android.com/samples/XYZTouristAttractions/Application/…
Shoeb Siddique

1
'Glide คล้ายกับ Picasso แต่เร็วกว่า Picasso มาก' และ 'Glide ใช้หน่วยความจำน้อยกว่า Picasso' ขอโทษฉันไม่ได้เนื้อหาที่มีความคิดเหล่านี้โปรดชอบที่จะลิงค์นี้: medium.com/@multidots/glide-vs-picasso-930eed42b81d แต่ฉันไม่สามารถทดสอบ Glide 4.0 ได้ในตอนนี้เพราะไม่สามารถอัพเกรด build เป็น 3.0.1 (การเชื่อมต่ออินเทอร์เน็ตช้า); แต่ฉันทำการทดสอบ Glide 3.5.2 เพื่อแสดง gif มันใช้งานได้ แต่ไม่สมบูรณ์อย่างที่ฉันคาดไว้บน img ขนาดใหญ่ (มันกะพริบ) และนี่ก็เป็นปัญหาทั่วไปเช่นกัน กรุณา CMIW
เหงียน Tan Dat

7

ใช้ImageViewExห้องสมุดที่ทำให้การใช้ GIF ImageViewเป็นเรื่องง่ายเหมือนการใช้


สิ่งนี้เลิกใช้แล้ว
Martin Marconcini

ฉันดูรหัสห้องสมุดนี้แล้วมันน่าจะดีสำหรับ 2 ปีหรือมากกว่านั้น
NightSkyCode

1
ใช่ฉันคิดว่ามันค่อนข้างดีเพียงแค่ชี้ให้เห็นว่าผู้เขียนตัดสินใจเลิกใช้แล้ว (และดังนั้นจึงไม่รักษา) ชี้ให้เห็นว่าเราควรใช้บางอย่างเช่น Picasso ซึ่งเป็นเรื่องแปลกเพราะ Picasso เป็นเครื่องมือดาวน์โหลดรูปภาพและจะไม่ช่วยคุณเมื่อมันแสดง gif เคลื่อนไหวอีกต่อไปกว่าการดาวน์โหลด / แคชจำนวนมาก เครื่องมือออกมี
Martin Marconcini

6

ลองทำตามนี้โค้ดตะโกนแสดงไฟล์ gif ใน ProgressBar

loading_activity.xml (ในโฟลเดอร์เค้าโครง)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff" >

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleLarge"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:indeterminate="true"
        android:indeterminateDrawable="@drawable/custom_loading"
        android:visibility="gone" />

</RelativeLayout>

custom_loading.xml (ในโฟลเดอร์ drawable)

ที่นี่ฉันใส่ black_gif.gif (ในโฟลเดอร์ drawable) คุณสามารถใส่ gif ของคุณเองที่นี่

<?xml version="1.0" encoding="utf-8"?>
<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/black_gif"
    android:pivotX="50%"
    android:pivotY="50%" />

LoadingActivity.java (ในโฟลเดอร์ res)

public class LoadingActivity extends Activity {

    ProgressBar bar;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_loading);
        bar = (ProgressBar) findViewById(R.id.progressBar);
        bar.setVisibility(View.VISIBLE);

    }

}

ฉันเห็นคุณหมุน gif ทำไม?
AlikElzin-kilaka

คุณจะใส่ gif ลงในโฟลเดอร์ drawable ได้อย่างไร?
Apurva


5

ฉันประสบความสำเร็จในการแก้ปัญหาที่เสนอในบทความนี้คลาสที่เรียกว่าGifMovieViewซึ่งทำให้Viewสามารถแสดงหรือเพิ่มไปยังเฉพาะViewGroupที่สามารถแสดงหรือเพิ่มที่เฉพาะเจาะจงลองดูวิธีการอื่นที่แสดงในส่วนที่ 2 และ 3 ของบทความที่ระบุ

ข้อเสียเปรียบเพียงอย่างเดียวของวิธีนี้คือการลดรอยหยักบนภาพยนตร์นั้นไม่ดี (ต้องเป็นผลข้างเคียงของการใช้ Android MovieClass "ที่ร่มรื่น" ) จากนั้นคุณจะดีกว่าตั้งค่าพื้นหลังเป็นสีทึบภายใน GIF แบบเคลื่อนไหวของคุณ


4

ความคิดบางอย่างในตัวอย่าง BitmapDecode ... โดยทั่วไปจะใช้คลาสภาพยนตร์โบราณ แต่ค่อนข้างไม่มีรูปแบบจาก android.graphics ในรุ่น API ล่าสุดที่คุณจำเป็นต้องปิดการเร่งความเร็วฮาร์ดแวร์ตามที่อธิบายไว้ที่นี่ มันก็เป็นการแยกสำหรับฉันเป็นอย่างอื่น

<activity
            android:hardwareAccelerated="false"
            android:name="foo.GifActivity"
            android:label="The state of computer animation 2014">
</activity>

นี่คือตัวอย่าง BitmapDecode ที่ย่อลงด้วยส่วน GIF เท่านั้น คุณต้องสร้างวิดเจ็ตของคุณเอง (ดู) และวาดด้วยตัวเอง ไม่ค่อยทรงพลังเท่า ImageView

import android.app.Activity;
import android.content.Context;
import android.graphics.*;
import android.os.*;
import android.view.View;

public class GifActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new GifView(this));
    }

    static class GifView extends View {
        Movie movie;

        GifView(Context context) {
            super(context);
            movie = Movie.decodeStream(
                    context.getResources().openRawResource(
                            R.drawable.some_gif));
        }
        @Override
        protected void onDraw(Canvas canvas) {   
            if (movie != null) {
                movie.setTime(
                    (int) SystemClock.uptimeMillis() % movie.duration());
                movie.draw(canvas, 0, 0);
                invalidate();
            }
        }
    }
}

2 วิธีอื่น ๆ อีกวิธีหนึ่งที่มี ImageView อีกอันที่มี WebView สามารถพบได้ในบทช่วยสอนนี้ วิธีการ ImageView ใช้ Apache -android gifview ที่ได้รับอนุญาตจาก Google Code


1
@luboz คุณช่วยกรุณาบอกฉันว่าวิธีการที่ดีสำหรับ Android ประสิทธิภาพ ... ฉันต้องการที่จะใช้ gif เคลื่อนไหวสำหรับแถบโหลดหน้าจอสแปล โปรดช่วยแนะนำวิธีการที่ดีกว่า นอกจากนี้ฉันต้องการทราบว่าโดยใช้วิธีการดูภาพเป็นไปได้หรือไม่ที่จะใช้คุณสมบัติประเภทของสเกล?
Swap-IOS-Android

4

@ PointerNull ให้ทางออกที่ดี แต่มันไม่สมบูรณ์แบบ มันไม่สามารถใช้งานได้กับอุปกรณ์บางตัวที่มีไฟล์ขนาดใหญ่และแสดงภาพเคลื่อนไหว Gif แบบ buggy พร้อมเดลต้าเฟรมในเวอร์ชัน ICS ก่อนหน้า ฉันพบวิธีแก้ปัญหาโดยไม่มีข้อบกพร่องนี้ มันเป็นห้องสมุดที่มีการถอดรหัสพื้นเมือง drawable: ของ Koral หุ่นยนต์-GIF-drawable


2

ใส่ไว้ใน WebView จะต้องสามารถแสดงได้อย่างถูกต้องเนื่องจากเบราว์เซอร์เริ่มต้นรองรับไฟล์ gif (Froyo + ถ้าฉันไม่ผิด)


นี่ไม่เป็นความจริง. ไม่ใช่ทุกเบราว์เซอร์ที่รองรับ GIF
RichieHH

4
หวังว่าเราไม่จำเป็นต้องใช้เบราว์เซอร์ทั้งหมด ... เบราว์เซอร์ในสต็อกรองรับตั้งแต่ Android 2.2 ดังนั้นโปรดอย่าลงคะแนนฉัน WebView จะแสดง GIF อย่างแน่นอน!
keybee

2

มีสองตัวเลือกในการโหลด gif แบบเคลื่อนไหวลงในแอพ Android ของเรา

1) การใช้Glideโหลด GIF ImageViewเข้า

    String urlGif = "https://cdn.dribbble.com/users/263558/screenshots/1337078/dvsd.gif";
    //add Glide implementation into the build.gradle file.
    ImageView imageView = (ImageView)findViewById(R.id.imageView);
    Uri uri = Uri.parse(urlGif);
    Glide.with(getApplicationContext()).load(uri).into(imageView);

2) ใช้ html เพื่อโหลด gif ลงใน a WebView

สร้าง html ด้วยที่อยู่ไปยังไฟล์. gif:

<html style="margin: 0;">
<body style="margin: 0;">
<img src="https://..../myimage.gif" style="width: 100%; height: 100%" />
</body>
</html>

เก็บไฟล์นี้ไว้ในไดเรกทอรีสินทรัพย์:

ป้อนคำอธิบายรูปภาพที่นี่

โหลด html นี้ลงใน WebView แอปพลิเคชันของคุณ:

    WebView webView =  (WebView)findViewById(R.id.webView);
    webView = (WebView) findViewById(R.id.webView);
    webView.loadUrl("file:///android_asset/html/webpage_gif.html");

ริเป็นตัวอย่างที่สมบูรณ์ของสองตัวเลือกนี้

ป้อนคำอธิบายรูปภาพที่นี่


2

สำหรับ android API (Android Pie) 28 และ + เท่านั้น

ใช้AnimatedImageDrawableเป็น

// ImageView from layout
val ima : ImageView = findViewById(R.id.img_gif)
// create AnimatedDrawable 
val decodedAnimation = ImageDecoder.decodeDrawable(
        // create ImageDecoder.Source object
        ImageDecoder.createSource(resources, R.drawable.tenor))
// set the drawble as image source of ImageView
ima.setImageDrawable(decodedAnimation)
// play the animation
(decodedAnimation as? AnimatedImageDrawable)?.start()

รหัส XML เพิ่ม ImageView

<ImageView
    android:id="@+id/img_gif"
    android:background="@drawable/ic_launcher_background" <!--Default background-->
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    android:layout_width="200dp"
    android:layout_height="200dp" />

AnimatedImageDrawable เป็นลูกของ Drawable และสร้างโดย ImageDecoder.decodeDrawable

ImageDecoder.decodeDrawableซึ่งต้องการอินสแตนซ์ของการImageDecoder.Sourceสร้างโดยImageDecoder.createSourceเพิ่มเติม

ImageDecoder.createSourceสามารถนำซอร์สมาเป็นชื่อ ByteBuffer, File, resourceId, URI, ContentResolver เพื่อสร้างออบเจ็กต์ต้นทางและใช้เพื่อสร้างAnimatedImageDrawableเป็นDrawable(การเรียก polymorphic)

static ImageDecoder.Source  createSource(AssetManager assets, String fileName)
static ImageDecoder.Source  createSource(ByteBuffer buffer)
static ImageDecoder.Source  createSource(File file)
static ImageDecoder.Source  createSource(Resources res, int resId)
static ImageDecoder.Source  createSource(ContentResolver cr, Uri uri)

หมายเหตุ: คุณยังสามารถสร้างBitmapใช้ImageDecoder # decodeBitmap

เอาท์พุท:

AnimatedDrawable ยังรองรับการปรับขนาดเฟรมและการจัดการสี


1

ฉันคิดว่าไลบรารีที่ดีกว่าในการจัดการไฟล์ gif ก็คืออันนี้: โดย koral

ใช้มันและฉันประสบความสำเร็จและห้องสมุดนี้อุทิศให้กับ GIF'S; แต่ที่ไหนที่เป็นปิกัสโซและร่อนเป็นกรอบรูปวัตถุประสงค์ทั่วไป ดังนั้นฉันคิดว่าผู้พัฒนาห้องสมุดนี้มีสมาธิกับไฟล์ gif ทั้งหมด



1

เพียงแค่ต้องการเพิ่มว่าคลาสMovieเลิกใช้แล้ว

คลาสนี้เลิกใช้แล้วในระดับ API P

ขอแนะนำให้ใช้สิ่งนี้

AnimatedImageDrawable

Drawable สำหรับการวาดภาพเคลื่อนไหว (เช่น GIF)


1

สร้างแพ็คเกจชื่อ“ Utils” ใต้โฟลเดอร์ src และสร้างคลาสที่ชื่อว่า“ GifImageView”

public class GifImageView extends View {
    private InputStream mInputStream;
    private Movie mMovie;
    private int mWidth, mHeight;
    private long mStart;
    private Context mContext;
    public GifImageView(Context context) {
        super(context);
        this.mContext = context;
    }
    public GifImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public GifImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        if (attrs.getAttributeName(1).equals("background")) {
            int id = Integer.parseInt(attrs.getAttributeValue(1).substring(1));
            setGifImageResource(id);
        }
    }
    private void init() {
        setFocusable(true);
        mMovie = Movie.decodeStream(mInputStream);
        mWidth = mMovie.width();
        mHeight = mMovie.height();
        requestLayout();
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(mWidth, mHeight);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        long now = SystemClock.uptimeMillis();
        if (mStart == 0) {
            mStart = now;
        }
        if (mMovie != null) {
            int duration = mMovie.duration();
            if (duration == 0) {
                duration = 1000;
            }
            int relTime = (int) ((now - mStart) % duration);
            mMovie.setTime(relTime);
            mMovie.draw(canvas, 0, 0);
            invalidate();
        }
    }
    public void setGifImageResource(int id) {
        mInputStream = mContext.getResources().openRawResource(id);
        init();
    }
    public void setGifImageUri(Uri uri) {
        try {
            mInputStream = mContext.getContentResolver().openInputStream(uri);
            init();
        } catch (FileNotFoundException e) {
            Log.e("GIfImageView", "File not found");
        }
    }
}

ตอนนี้กำหนด GifImageView ในไฟล์ MainActivity: src / activity / MainActivity.class

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        GifImageView gifImageView = (GifImageView) findViewById(R.id.GifImageView);
        gifImageView.setGifImageResource(R.drawable.smartphone_drib);
    }
}

ตอนนี้ไฟล์ส่วน UI: res / layout / activity_main.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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:gravity="center"
    android:background="#111E39"
    tools:context=".Activity.MainActivity">
    <com.android.animatedgif.Utils.GifImageView
        android:id="@+id/GifImageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true" />

</RelativeLayout>

รหัส Resourece: ตัวอย่าง Android


วิธีปรับขนาดรูปภาพกรุณา
The Dead Guy

0

ก่อนอื่นเบราว์เซอร์ Android ควรสนับสนุน GIF แบบเคลื่อนไหว หากไม่เป็นเช่นนั้นก็เป็นข้อผิดพลาด! ดูที่ตัวติดตามปัญหา

หากคุณกำลังแสดงภาพเคลื่อนไหว GIF เหล่านี้นอกเบราว์เซอร์อาจเป็นเรื่องที่แตกต่าง หากต้องการทำสิ่งที่คุณขอจะต้องใช้ไลบรารีภายนอกที่รองรับการถอดรหัส Animated GIFs

พอร์ตแรกของการโทรคือดู API Java2D หรือ JAI (Java Advanced Imaging) แม้ว่าฉันจะประหลาดใจมากถ้า Android Dalvik จะสนับสนุนไลบรารีเหล่านั้นในแอปของคุณ


อุปกรณ์บางตัวไม่แสดงภาพเคลื่อนไหว gif แม้กระทั่งบนเว็บเบราว์เซอร์ ตัวอย่างเช่นฉันรู้ว่า galaxy mini พร้อม android 2.3.6 ไม่แสดงพวกเขา
นักพัฒนา android

0

คล้ายกับสิ่งที่ @Leonti พูด แต่มีความลึกมากกว่านี้เล็กน้อย:

สิ่งที่ฉันทำเพื่อแก้ไขปัญหาเดียวกันคือเปิด GIMP, ซ่อนเลเยอร์ทั้งหมดยกเว้นอันใดอันหนึ่งส่งออกเป็นรูปภาพของตัวเองแล้วซ่อนเลเยอร์นั้นและยกเลิกการซ่อนเลเยอร์ถัดไปเป็นต้นจนกว่าฉันจะมีไฟล์ทรัพยากรแต่ละไฟล์สำหรับแต่ละไฟล์ . จากนั้นฉันสามารถใช้เป็นเฟรมในไฟล์ AnimationDrawable XML


1
คุณสามารถใช้gifsicleเพื่อแยกเฟรมหากคุณไม่มีเวลามาก หลามหรือทุบตีก็มีประโยชน์มากเมื่อสร้างไฟล์ xml
lubosz

0

ฉันแก้ไขมันได้โดยการแบ่ง gif ในเฟรมและใช้แอนิเมชั่น Android มาตรฐาน


0

สิ่งที่ฉันทำเพื่อแสดง gif ในแอพ ฉันขยาย ImageView เพื่อให้ผู้คนสามารถใช้คุณสมบัติได้อย่างอิสระ มันสามารถแสดง gif จาก url หรือจากไดเรกทอรีสินทรัพย์ ห้องสมุดยังช่วยให้การขยายคลาสเพื่อสืบทอดจากมันและขยายเพื่อสนับสนุนวิธีการต่าง ๆ ในการเริ่มต้น gif

https://github.com/Gavras/GIFView

มีคำแนะนำเล็กน้อยในหน้า GitHub

มันถูกตีพิมพ์ใน Android Arsenal:

https://android-arsenal.com/details/1/4947

ใช้ตัวอย่าง:

จาก XML:

<com.whygraphics.gifview.gif.GIFView xmlns:gif_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/main_activity_gif_vie"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:scaleType="center"
        gif_view:gif_src="url:http://pop.h-cdn.co/assets/16/33/480x264/gallery-1471381857-gif-season-2.gif" />

ในกิจกรรม:

    GIFView mGifView = (GIFView) findViewById(R.id.main_activity_gif_vie);

    mGifView.setOnSettingGifListener(new GIFView.OnSettingGifListener() {
                @Override
                public void onSuccess(GIFView view, Exception e) {
                    Toast.makeText(MainActivity.this, "onSuccess()", Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onFailure(GIFView view, Exception e) {

        }
});

การตั้งค่า gif โดยทางโปรแกรม:

mGifView.setGifResource("asset:gif1");

0

วิธีที่ง่ายที่สุด - สามารถพิจารณารหัสด้านล่าง

เราสามารถใช้ประโยชน์จาก Imageview setImageResource อ้างอิงรหัสด้านล่างสำหรับเดียวกัน

รหัสด้านล่างสามารถใช้เพื่อแสดงภาพเช่น gif ใส่ในกรณีที่คุณมีภาพแยกหลายภาพของ gif เพียงแค่แบ่ง gif เป็น png แยกจากเครื่องมือออนไลน์แล้ววางรูปภาพลงใน drawable ตามลำดับด้านล่าง

image_1.png, image_2.png ฯลฯ

มีตัวจัดการเพื่อเปลี่ยนภาพแบบไดนามิก

int imagePosition = 1;
    Handler handler = new Handler();
        Runnable runnable = new Runnable() {
            public void run() {
                updateImage();
            }
        };




    public void updateImage() {

                appInstance.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        int resId = getResources().getIdentifier("image_" + imagePosition, "drawable", appInstance.getPackageName());
                        gifImageViewDummy.setImageResource(resId);
                        imagePosition++;
    //Consider you have 30 image for the anim
                        if (imagePosition == 30) {
//this make animation play only once
                            handler.removeCallbacks(runnable);

                        } else {
    //You can define your own time based on the animation
                            handler.postDelayed(runnable, 50);
                        }

//to make animation to continue use below code and remove above if else
// if (imagePosition == 30)
//imagePosition = 1;
// handler.postDelayed(runnable, 50);
// 
                    }
                });
              }

0

วิธีง่ายๆในการแสดง GIF แบบเคลื่อนไหวโดยตรงจาก URL ไปยังเค้าโครงแอปของคุณคือการใช้คลาส WebView

ขั้นตอนที่ 1: ในรูปแบบ XML ของคุณ

<WebView
android:id="@+id/webView"
android:layout_width="50dp"
android:layout_height="50dp"
/>

ขั้นตอนที่ 2: ในกิจกรรมของคุณ

WebView wb;
wb = (WebView) findViewById(R.id.webView);
wb.loadUrl("https://.......);

ขั้นตอนที่ 3: ในรายการของคุณ XML ให้สิทธิ์อินเทอร์เน็ต

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

ขั้นตอนที่ 4: ในกรณีที่คุณต้องการทำให้พื้นหลัง GIF ของคุณโปร่งใสและทำให้ GIF เหมาะสมกับการออกแบบ

wb.setBackgroundColor(Color.TRANSPARENT);
wb.getSettings().setLoadWithOverviewMode(true);
wb.getSettings().setUseWideViewPort(true);


ขอบคุณสำหรับการแก้ปัญหา แต่ผู้พัฒนาจำนวนมากอาจไม่ต้องการให้เรนเดอร์ไม่ใช่แบบดั้งเดิมเนื่องจากมีผลกระทบต่อหน่วยความจำการใช้งาน CPU และอายุการใช้งานแบตเตอรี่
ruX


-8
public class Test extends GraphicsActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(new SampleView(this));
  }

  private static class SampleView extends View {
    private Bitmap mBitmap;
    private Bitmap mBitmap2;
    private Bitmap mBitmap3;
    private Bitmap mBitmap4;
    private Drawable mDrawable;

    private Movie mMovie;
    private long mMovieStart;

    // Set to false to use decodeByteArray
    private static final boolean DECODE_STREAM = true;

    private static byte[] streamToBytes(InputStream is) {
      ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
      byte[] buffer = new byte[1024];
      int len;
      try {
        while ((len = is.read(buffer)) >= 0) {
          os.write(buffer, 0, len);
        }
      } catch (java.io.IOException e) {
      }
      return os.toByteArray();
    }

    public SampleView(Context context) {
      super(context);
      setFocusable(true);

      java.io.InputStream is;
      is = context.getResources().openRawResource(R.drawable.icon);

      BitmapFactory.Options opts = new BitmapFactory.Options();
      Bitmap bm;

      opts.inJustDecodeBounds = true;
      bm = BitmapFactory.decodeStream(is, null, opts);

      // now opts.outWidth and opts.outHeight are the dimension of the
      // bitmap, even though bm is null

      opts.inJustDecodeBounds = false; // this will request the bm
      opts.inSampleSize = 4; // scaled down by 4
      bm = BitmapFactory.decodeStream(is, null, opts);

      mBitmap = bm;

      // decode an image with transparency
      is = context.getResources().openRawResource(R.drawable.icon);
      mBitmap2 = BitmapFactory.decodeStream(is);

      // create a deep copy of it using getPixels() into different configs
      int w = mBitmap2.getWidth();
      int h = mBitmap2.getHeight();
      int[] pixels = new int[w * h];
      mBitmap2.getPixels(pixels, 0, w, 0, 0, w, h);
      mBitmap3 = Bitmap.createBitmap(pixels, 0, w, w, h,
          Bitmap.Config.ARGB_8888);
      mBitmap4 = Bitmap.createBitmap(pixels, 0, w, w, h,
          Bitmap.Config.ARGB_4444);

      mDrawable = context.getResources().getDrawable(R.drawable.icon);
      mDrawable.setBounds(150, 20, 300, 100);

      is = context.getResources().openRawResource(R.drawable.animated_gif);

      if (DECODE_STREAM) {
        mMovie = Movie.decodeStream(is);
      } else {
        byte[] array = streamToBytes(is);
        mMovie = Movie.decodeByteArray(array, 0, array.length);
      }
    }

    @Override
    protected void onDraw(Canvas canvas) {
      canvas.drawColor(0xFFCCCCCC);

      Paint p = new Paint();
      p.setAntiAlias(true);

      canvas.drawBitmap(mBitmap, 10, 10, null);
      canvas.drawBitmap(mBitmap2, 10, 170, null);
      canvas.drawBitmap(mBitmap3, 110, 170, null);
      canvas.drawBitmap(mBitmap4, 210, 170, null);

      mDrawable.draw(canvas);

      long now = android.os.SystemClock.uptimeMillis();
      if (mMovieStart == 0) { // first time
        mMovieStart = now;
      }
      if (mMovie != null) {
        int dur = mMovie.duration();
        if (dur == 0) {
          dur = 1000;
        }
        int relTime = (int) ((now - mMovieStart) % dur);
        mMovie.setTime(relTime);
        mMovie.draw(canvas, getWidth() - mMovie.width(), getHeight()
            - mMovie.height());
        invalidate();
      }
    }
  }
}

class GraphicsActivity extends Activity {
  // set to true to test Picture
  private static final boolean TEST_PICTURE = false;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
  }

  @Override
  public void setContentView(View view) {
    if (TEST_PICTURE) {
      ViewGroup vg = new PictureLayout(this);
      vg.addView(view);
      view = vg;
    }

    super.setContentView(view);
  }
}

class PictureLayout extends ViewGroup {
  private final Picture mPicture = new Picture();

  public PictureLayout(Context context) {
    super(context);
  }

  public PictureLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  @Override
  public void addView(View child) {
    if (getChildCount() > 1) {
      throw new IllegalStateException(
          "PictureLayout can host only one direct child");
    }

    super.addView(child);
  }

  @Override
  public void addView(View child, int index) {
    if (getChildCount() > 1) {
      throw new IllegalStateException(
          "PictureLayout can host only one direct child");
    }

    super.addView(child, index);
  }

  @Override
  public void addView(View child, LayoutParams params) {
    if (getChildCount() > 1) {
      throw new IllegalStateException(
          "PictureLayout can host only one direct child");
    }

    super.addView(child, params);
  }

  @Override
  public void addView(View child, int index, LayoutParams params) {
    if (getChildCount() > 1) {
      throw new IllegalStateException(
          "PictureLayout can host only one direct child");
    }

    super.addView(child, index, params);
  }

  @Override
  protected LayoutParams generateDefaultLayoutParams() {
    return new LayoutParams(LayoutParams.MATCH_PARENT,
        LayoutParams.MATCH_PARENT);
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    final int count = getChildCount();

    int maxHeight = 0;
    int maxWidth = 0;

    for (int i = 0; i < count; i++) {
      final View child = getChildAt(i);
      if (child.getVisibility() != GONE) {
        measureChild(child, widthMeasureSpec, heightMeasureSpec);
      }
    }

    maxWidth += getPaddingLeft() + getPaddingRight();
    maxHeight += getPaddingTop() + getPaddingBottom();

    Drawable drawable = getBackground();
    if (drawable != null) {
      maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
      maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
    }

    setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec),
        resolveSize(maxHeight, heightMeasureSpec));
  }

  private void drawPict(Canvas canvas, int x, int y, int w, int h, float sx,
      float sy) {
    canvas.save();
    canvas.translate(x, y);
    canvas.clipRect(0, 0, w, h);
    canvas.scale(0.5f, 0.5f);
    canvas.scale(sx, sy, w, h);
    canvas.drawPicture(mPicture);
    canvas.restore();
  }

  @Override
  protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(mPicture.beginRecording(getWidth(), getHeight()));
    mPicture.endRecording();

    int x = getWidth() / 2;
    int y = getHeight() / 2;

    if (false) {
      canvas.drawPicture(mPicture);
    } else {
      drawPict(canvas, 0, 0, x, y, 1, 1);
      drawPict(canvas, x, 0, x, y, -1, 1);
      drawPict(canvas, 0, y, x, y, 1, -1);
      drawPict(canvas, x, y, x, y, -1, -1);
    }
  }

  @Override
  public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
    location[0] = getLeft();
    location[1] = getTop();
    dirty.set(0, 0, getWidth(), getHeight());
    return getParent();
  }

  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    final int count = super.getChildCount();

    for (int i = 0; i < count; i++) {
      final View child = getChildAt(i);
      if (child.getVisibility() != GONE) {
        final int childLeft = getPaddingLeft();
        final int childTop = getPaddingTop();
        child.layout(childLeft, childTop,
            childLeft + child.getMeasuredWidth(),
            childTop + child.getMeasuredHeight());

      }
    }
  }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.