Gif动态图加载

article/2025/9/29 12:11:58

【Android Demo】加载.gif格式图片

Android系统为了节省内存,一般不支持直接显示gif图片,即使你强制设置了,也只会显示图片的第一帧。

这个 Demo 是在网上看到的,是个思路,还是有些局限性,还是记录下,以后研究吧。

1.效果图:

2.实现原理:

通过解码gif图片形成多张静态图,然后通过线程和imageView来实现循环播放

 

3.准备GIF图片

 

4.详细代码

<1>工具类:CommonUtil.java

复制代码
package com.yanis.tools;import java.io.InputStream;import com.yanis.tools.GifHelper.GifFrame;public class CommonUtil {  /** * 解码GIF图片 *  * @param is * @return */  public static GifFrame[] getGif(InputStream is) {  GifHelper gifHelper = new GifHelper();  if (GifHelper.STATUS_OK == gifHelper.read(is)) {  return gifHelper.getFrames();  }  return null;  }  /** * 判断图片是否为GIF格式 * @param is * @return */  public static boolean isGif(InputStream is) {  GifHelper gifHelper = new GifHelper();  return gifHelper.isGif(is);  }  
}
复制代码

<2>解码类:GifHelper.java

复制代码
package com.yanis.tools;import java.io.InputStream;
import java.util.Vector;import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;//Handler for read & extract Bitmap from *.gif  
public class GifHelper {  // to store *.gif data, Bitmap & delay  public static class GifFrame {  // to access image & delay w/o interface  public Bitmap image;  public int delay;  public GifFrame(Bitmap im, int del) {  image = im;  delay = del;  }  }  // to define some error type  public static final int STATUS_OK = 0;  public static final int STATUS_FORMAT_ERROR = 1;  public static final int STATUS_OPEN_ERROR = 2;  protected int status;  protected InputStream in;  protected int width; // full image width  protected int height; // full image height  protected boolean gctFlag; // global color table used  protected int gctSize; // size of global color table  protected int loopCount = 1; // iterations; 0 = repeat forever  protected int[] gct; // global color table  protected int[] lct; // local color table  protected int[] act; // active color table  protected int bgIndex; // background color index  protected int bgColor; // background color  protected int lastBgColor; // previous bg color  protected int pixelAspect; // pixel aspect ratio  protected boolean lctFlag; // local color table flag  protected boolean interlace; // interlace flag  protected int lctSize; // local color table size  protected int ix, iy, iw, ih; // current image rectangle  protected int lrx, lry, lrw, lrh;  protected Bitmap image; // current frame  protected Bitmap lastImage; // previous frame  protected int frameindex = 0;  public int getFrameindex() {  return frameindex;  }  public void setFrameindex(int frameindex) {  this.frameindex = frameindex;  if (frameindex > frames.size() - 1) {  frameindex = 0;  }  }  protected byte[] block = new byte[256]; // current data block  protected int blockSize = 0; // block size  // last graphic control extension info  protected int dispose = 0;  // 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev  protected int lastDispose = 0;  protected boolean transparency = false; // use transparent color  protected int delay = 0; // delay in milliseconds  protected int transIndex; // transparent color index  protected static final int MaxStackSize = 4096;  // max decoder pixel stack size  // LZW decoder working arrays  protected short[] prefix;  protected byte[] suffix;  protected byte[] pixelStack;  protected byte[] pixels;  protected Vector<GifFrame> frames; // frames read from current file  protected int frameCount;  // to get its Width / Height  public int getWidth() {  return width;  }  public int getHeigh() {  return height;  }  /** * Gets display duration for specified frame. *  * @param n *            int index of frame * @return delay in milliseconds */  public int getDelay(int n) {  delay = -1;  if ((n >= 0) && (n < frameCount)) {  delay = ((GifFrame) frames.elementAt(n)).delay;  }  return delay;  }  public int getFrameCount() {  return frameCount;  }  public Bitmap getImage() {  return getFrame(0);  }  public int getLoopCount() {  return loopCount;  }  protected void setPixels() {  int[] dest = new int[width * height];  // fill in starting image contents based on last image's dispose code  if (lastDispose > 0) {  if (lastDispose == 3) {  // use image before last  int n = frameCount - 2;  if (n > 0) {  lastImage = getFrame(n - 1);  } else {  lastImage = null;  }  }  if (lastImage != null) {  lastImage.getPixels(dest, 0, width, 0, 0, width, height);  // copy pixels  if (lastDispose == 2) {  // fill last image rect area with background color  int c = 0;  if (!transparency) {  c = lastBgColor;  }  for (int i = 0; i < lrh; i++) {  int n1 = (lry + i) * width + lrx;  int n2 = n1 + lrw;  for (int k = n1; k < n2; k++) {  dest[k] = c;  }  }  }  }  }  // copy each source line to the appropriate place in the destination  int pass = 1;  int inc = 8;  int iline = 0;  for (int i = 0; i < ih; i++) {  int line = i;  if (interlace) {  if (iline >= ih) {  pass++;  switch (pass) {  case 2:  iline = 4;  break;  case 3:  iline = 2;  inc = 4;  break;  case 4:  iline = 1;  inc = 2;  }  }  line = iline;  iline += inc;  }  line += iy;  if (line < height) {  int k = line * width;  int dx = k + ix; // start of line in dest  int dlim = dx + iw; // end of dest line  if ((k + width) < dlim) {  dlim = k + width; // past dest edge  
              }  int sx = i * iw; // start of line in source  while (dx < dlim) {  // map color and insert in destination  int index = ((int) pixels[sx++]) & 0xff;  int c = act[index];  if (c != 0) {  dest[dx] = c;  }  dx++;  }  }  }  image = Bitmap.createBitmap(dest, width, height, Config.RGB_565);  }  public Bitmap getFrame(int n) {  Bitmap im = null;  if ((n >= 0) && (n < frameCount)) {  im = ((GifFrame) frames.elementAt(n)).image;  }  return im;  }  public GifFrame[] getFrames() {  if(null != frames) return frames.toArray(new GifFrame[0]);  return null;  }  public Bitmap nextBitmap() {  frameindex++;  if (frameindex > frames.size() - 1) {  frameindex = 0;  }  return ((GifFrame) frames.elementAt(frameindex)).image;  }  public int nextDelay() {  return ((GifFrame) frames.elementAt(frameindex)).delay;  }  // to read & parse all *.gif stream  public int read(InputStream is) {  init();  if (is != null) {  in = is;  readHeader();  if (!err()) {  readContents();  if (frameCount < 0) {  status = STATUS_FORMAT_ERROR;  }  }  } else {  status = STATUS_OPEN_ERROR;  }  try {  is.close();  } catch (Exception e) {  e.printStackTrace();  }  return status;  }  public boolean isGif(InputStream is) {  init();  if (is != null) {  in = is;  String id = "";  for (int i = 0; i < 6; i++) {  id += (char) read();  }  if (!id.toUpperCase().startsWith("GIF")) {  return false;  }  }  try {  is.close();  } catch (Exception e) {  e.printStackTrace();  }  return true;  }  protected void decodeImageData() {  int NullCode = -1;  int npix = iw * ih;  int available, clear, code_mask, code_size, end_of_information, in_code, old_code, bits, code, count, i, datum, data_size, first, top, bi, pi;  if ((pixels == null) || (pixels.length < npix)) {  pixels = new byte[npix]; // allocate new pixel array  
      }  if (prefix == null) {  prefix = new short[MaxStackSize];  }  if (suffix == null) {  suffix = new byte[MaxStackSize];  }  if (pixelStack == null) {  pixelStack = new byte[MaxStackSize + 1];  }  // Initialize GIF data stream decoder.  data_size = read();  clear = 1 << data_size;  end_of_information = clear + 1;  available = clear + 2;  old_code = NullCode;  code_size = data_size + 1;  code_mask = (1 << code_size) - 1;  for (code = 0; code < clear; code++) {  prefix[code] = 0;  suffix[code] = (byte) code;  }  // Decode GIF pixel stream.  datum = bits = count = first = top = pi = bi = 0;  for (i = 0; i < npix;) {  if (top == 0) {  if (bits < code_size) {  // Load bytes until there are enough bits for a code.  if (count == 0) {  // Read a new data block.  count = readBlock();  if (count <= 0) {  break;  }  bi = 0;  }  datum += (((int) block[bi]) & 0xff) << bits;  bits += 8;  bi++;  count--;  continue;  }  // Get the next code.  code = datum & code_mask;  datum >>= code_size;  bits -= code_size;  // Interpret the code  if ((code > available) || (code == end_of_information)) {  break;  }  if (code == clear) {  // Reset decoder.  code_size = data_size + 1;  code_mask = (1 << code_size) - 1;  available = clear + 2;  old_code = NullCode;  continue;  }  if (old_code == NullCode) {  pixelStack[top++] = suffix[code];  old_code = code;  first = code;  continue;  }  in_code = code;  if (code == available) {  pixelStack[top++] = (byte) first;  code = old_code;  }  while (code > clear) {  pixelStack[top++] = suffix[code];  code = prefix[code];  }  first = ((int) suffix[code]) & 0xff;  // Add a new string to the string table,  if (available >= MaxStackSize) {  break;  }  pixelStack[top++] = (byte) first;  prefix[available] = (short) old_code;  suffix[available] = (byte) first;  available++;  if (((available & code_mask) == 0)  && (available < MaxStackSize)) {  code_size++;  code_mask += available;  }  old_code = in_code;  }  // Pop a pixel off the pixel stack.  top--;  pixels[pi++] = pixelStack[top];  i++;  }  for (i = pi; i < npix; i++) {  pixels[i] = 0; // clear missing pixels  
      }  }  protected boolean err() {  return status != STATUS_OK;  }  // to initia variable  protected void init() {  status = STATUS_OK;  frameCount = 0;  frames = new Vector<GifFrame>();  gct = null;  lct = null;  }  protected int read() {  int curByte = 0;  try {  curByte = in.read();  } catch (Exception e) {  status = STATUS_FORMAT_ERROR;  }  return curByte;  }  protected int readBlock() {  blockSize = read();  int n = 0;  if (blockSize > 0) {  try {  int count = 0;  while (n < blockSize) {  count = in.read(block, n, blockSize - n);  if (count == -1) {  break;  }  n += count;  }  } catch (Exception e) {  e.printStackTrace();  }  if (n < blockSize) {  status = STATUS_FORMAT_ERROR;  }  }  return n;  }  // Global Color Table  protected int[] readColorTable(int ncolors) {  int nbytes = 3 * ncolors;  int[] tab = null;  byte[] c = new byte[nbytes];  int n = 0;  try {  n = in.read(c);  } catch (Exception e) {  e.printStackTrace();  }  if (n < nbytes) {  status = STATUS_FORMAT_ERROR;  } else {  tab = new int[256]; // max size to avoid bounds checks  int i = 0;  int j = 0;  while (i < ncolors) {  int r = ((int) c[j++]) & 0xff;  int g = ((int) c[j++]) & 0xff;  int b = ((int) c[j++]) & 0xff;  tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b;  }  }  return tab;  }  // Image Descriptor  protected void readContents() {  // read GIF file content blocks  boolean done = false;  while (!(done || err())) {  int code = read();  switch (code) {  case 0x2C: // image separator  
              readImage();  break;  case 0x21: // extension  code = read();  switch (code) {  case 0xf9: // graphics control extension  
                  readGraphicControlExt();  break;  case 0xff: // application extension  
                  readBlock();  String app = "";  for (int i = 0; i < 11; i++) {  app += (char) block[i];  }  if (app.equals("NETSCAPE2.0")) {  readNetscapeExt();  } else {  skip(); // don't care  
                  }  break;  default: // uninteresting extension  
                  skip();  }  break;  case 0x3b: // terminator  done = true;  break;  case 0x00: // bad byte, but keep going and see what happens  break;  default:  status = STATUS_FORMAT_ERROR;  }  }  }  protected void readGraphicControlExt() {  read(); // block size  int packed = read(); // packed fields  dispose = (packed & 0x1c) >> 2; // disposal method  if (dispose == 0) {  dispose = 1; // elect to keep old image if discretionary  
      }  transparency = (packed & 1) != 0;  delay = readShort() * 10; // delay in milliseconds  transIndex = read(); // transparent color index  read(); // block terminator  
  }  // to get Stream - Head  protected void readHeader() {  String id = "";  for (int i = 0; i < 6; i++) {  id += (char) read();  }  if (!id.toUpperCase().startsWith("GIF")) {  status = STATUS_FORMAT_ERROR;  return;  }  readLSD();  if (gctFlag && !err()) {  gct = readColorTable(gctSize);  bgColor = gct[bgIndex];  }  }  protected void readImage() {  // offset of X  ix = readShort(); // (sub)image position & size  // offset of Y  iy = readShort();  // width of bitmap  iw = readShort();  // height of bitmap  ih = readShort();  // Local Color Table Flag  int packed = read();  lctFlag = (packed & 0x80) != 0; // 1 - local color table flag  // Interlace Flag, to array with interwoven if ENABLE, with order  // otherwise  interlace = (packed & 0x40) != 0; // 2 - interlace flag  // 3 - sort flag  // 4-5 - reserved  lctSize = 2 << (packed & 7); // 6-8 - local color table size  if (lctFlag) {  lct = readColorTable(lctSize); // read table  act = lct; // make local table active  } else {  act = gct; // make global table active  if (bgIndex == transIndex) {  bgColor = 0;  }  }  int save = 0;  if (transparency) {  save = act[transIndex];  act[transIndex] = 0; // set transparent color if specified  
      }  if (act == null) {  status = STATUS_FORMAT_ERROR; // no color table defined  
      }  if (err()) {  return;  }  decodeImageData(); // decode pixel data  
      skip();  if (err()) {  return;  }  frameCount++;  // create new image to receive frame data  image = Bitmap.createBitmap(width, height, Config.RGB_565);  // createImage(width, height);  setPixels(); // transfer pixel data to image  frames.addElement(new GifFrame(image, delay)); // add image to frame  // list  if (transparency) {  act[transIndex] = save;  }  resetFrame();  }  // Logical Screen Descriptor  protected void readLSD() {  // logical screen size  width = readShort();  height = readShort();  // packed fields  int packed = read();  gctFlag = (packed & 0x80) != 0; // 1 : global color table flag  // 2-4 : color resolution  // 5 : gct sort flag  gctSize = 2 << (packed & 7); // 6-8 : gct size  bgIndex = read(); // background color index  pixelAspect = read(); // pixel aspect ratio  
  }  protected void readNetscapeExt() {  do {  readBlock();  if (block[0] == 1) {  // loop count sub-block  int b1 = ((int) block[1]) & 0xff;  int b2 = ((int) block[2]) & 0xff;  loopCount = (b2 << 8) | b1;  }  } while ((blockSize > 0) && !err());  }  // read 8 bit data  protected int readShort() {  // read 16-bit value, LSB first  return read() | (read() << 8);  }  protected void resetFrame() {  lastDispose = dispose;  lrx = ix;  lry = iy;  lrw = iw;  lrh = ih;  lastImage = image;  lastBgColor = bgColor;  dispose = 0;  transparency = false;  delay = 0;  lct = null;  }  /** * Skips variable length blocks up to and including next zero length block. */  protected void skip() {  do {  readBlock();  } while ((blockSize > 0) && !err());  }  
}  
复制代码

<3>Activity显示:MainActivity.java

复制代码
package com.yanis.gifphoto;import java.io.FileInputStream;
import java.io.InputStream;import android.app.Activity;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;import com.yanis.tools.CommonUtil;
import com.yanis.tools.GifHelper.GifFrame;public class MainActivity extends Activity {private PlayGifTask mGifTask;  ImageView iv;  GifFrame[] frames;  FileInputStream fis=null;  @Override  protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  iv = new ImageView(this);  iv.setScaleType(ScaleType.CENTER);  setContentView(iv, new LayoutParams(LayoutParams.MATCH_PARENT,  LayoutParams.MATCH_PARENT));  //对Gif图片进行解码  final InputStream fis = getResources().openRawResource(R.drawable.gidpic);  /*try { fis = new FileInputStream(new File(Environment.getExternalStorageDirectory()+File.separator+"/tmp/111.gif")); } catch (FileNotFoundException e) { e.printStackTrace(); }*/  frames = CommonUtil.getGif(fis);  mGifTask = new PlayGifTask(iv, frames);  mGifTask.startTask();  Thread th=new Thread(mGifTask);  th.start();  }  @Override  protected void onDestroy() {  super.onDestroy();  if(null != mGifTask) mGifTask.stopTask();  }  //用来循环播放Gif每帧图片  private class PlayGifTask implements Runnable {  int i = 0;  ImageView iv;  GifFrame[] frames;  int framelen,oncePlayTime=0;  public PlayGifTask(ImageView iv, GifFrame[] frames) {  this.iv = iv;  this.frames = frames;  int n=0;  framelen=frames.length;  while(n<framelen){  oncePlayTime+=frames[n].delay;  n++;  }  Log.d("msg", "playTime= "+oncePlayTime);  //Gif图片单次播放时长
             }  Handler h2=new Handler(){  public void handleMessage(Message msg) {  switch(msg.what){  case 1:  iv.setImageBitmap((Bitmap)msg.obj);  break;  }  };  };  @Override  public void run() {  if (!frames[i].image.isRecycled()) {  //      iv.setImageBitmap(frames[i].image);  Message m=Message.obtain(h2, 1, frames[i].image);  m.sendToTarget();  }  iv.postDelayed(this, frames[i++].delay);  i %= framelen;    }  public void startTask() {  iv.post(this);  }  public void stopTask() {  if(null != iv) iv.removeCallbacks(this);  iv = null;  if(null != frames) {  for(GifFrame frame : frames) {  if(frame.image != null && !frame.image.isRecycled()) {  frame.image.recycle();  frame.image = null;  }  }  frames = null;  //      mGifTask=null;  
            }  }  }  
}
复制代码

 

 

源代码地址:https://github.com/YeXiaoChao/Yc_demo_gifphoto

来源:http://blog.csdn.net/yudajun/article/details/7865653


http://chatgpt.dhexx.cn/article/IE09fD5V.shtml

相关文章

如何在CSDN博客中加入GIF动图

快速导航 下载GifGam如何使用GifGam 前言&#xff1a; 可能很多人看到别人的博客都有动态的效果展示图&#xff0c;今天分享一个个人感觉特别好用的一个工具&#xff01;非常小只有1.6MB&#xff0c;GifGam这个小工具 下载GifGam 下载方法一&#xff1a; 百度网盘下载链接 链接…

高清美图和GIF动图素材网站推荐,拿走不谢!

在这里&#xff0c;我整理了一些高大上、高清图片和GIF动图的素材网站&#xff0c;分享给大家。 身为设计师&#xff0c;图片素材是必备的。你在某处发现一张很漂亮的图片&#xff0c;无奈人家加了水印&#xff0c;然后你去百度&#xff0c;可是就像大海捞针一样&#xff0c;根…

一款免费的GIF动图制作工具,关键无水印

做动态图片&#xff0c;你是不是先用录屏工具&#xff0c;录制MP4视频文件&#xff0c;再把视频转换工具转成GIF 现在不用这么麻烦了&#xff0c;今天分享一款GIF一键制作软件&#xff0c;省略视频步骤&#xff0c;电脑PC端的软件&#xff0c;下载方法见文末。 VeryCapture 界…

怎么制作QQ动态表情包,GIF出处是哪

现在不管是聊天还是看视频&#xff0c;都少不了gif动图&#xff0c;制作gif动图的软件也越来越多&#xff0c;但是我们平时使用的GIF一般都是别人制作好的&#xff0c;有没有想自己制作GIF的冲动呢&#xff1f;那怎么制作QQ动态表情包&#xff0c;GIF出处是哪?今天带大家一起看…

推荐一个免费GIF动图下载的网站

在开发过程中难免遇到耗时操作,耗时操作往往导致线程阻塞,通常这种情况可以采用开线程的方式解决,即将耗时操作放入新线程中,同时在UI线程中加一个GIF动图即可。可作为后端开发工程师来讲单独设计一个GIF图实在是意义不大(懒+不会)。 好在现在发现了这个GIF免费下载的网站…

动图gif怎么制作?制作方法你学会了吗?

动图gif怎么制作&#xff1f;相信很多小伙伴们在日常生活中都有需要制作动图的时候&#xff0c;因为毕竟网上保存的动图可能无法表达我们所要表达的意思。说到这里可能很多小伙伴们会觉得自己制作给gif还是比较困难的&#xff0c;其实不然&#xff0c;只要我们选择一个合适的辅…

动图

动图&#xff0c;也叫“影图”&#xff0c;英文称为 Cinemagraph 或 Motionimage。文件格式常为传统的 GIF Graphics Interchange Format文件。 动图的初衷就是为静态图片添加一些细微的、局部的运动&#xff0c;给人一种“世间静谧&#xff0c;唯它悄动”的感觉。 ◆ ◆ ◆ 历…

51GIF GIF动图中文搜索平台,表情包发源地,GIF动图素材库

51GIF是中国最具人气的GIF搜索引擎,是国内专业的GIF动图中文搜索平台&#xff0c;是自媒体素材库&#xff0c;野表情发源地&#xff0c;GIF动图在线制作工具&#xff0c;动态表情包发源地&#xff1a;http://www.51gif.com/ 1、搜索专业 51GIF毫不夸张地说拥有国内最大最全…

被封杀4年的看片神器终于解禁了,要跟百度网盘抢生意?

&#xff08;关注我&#xff0c;带你看~&#xff09; 说到看片工具&#xff0c;大家第一个想到的或许是快播。 自从快播没落以后&#xff0c;迅雷就成了广大宅男必备的下片神器&#xff0c;一直以来都广受人们喜爱。 对于习惯使用手机看片的小伙伴来说&#xff0c;iOS和安卓端的…

人人影视 for Mac(美剧电影必备神器)

人人影视 Mac客户端是Mac平台上一款观看美剧电影必备的神器&#xff01;人人影视 for Mac网络全网海量电影&#xff0c;电视&#xff0c;动漫&#xff0c;综艺等影视资源&#xff0c;无需会员即可观看&#xff01;想要看各种美剧&#xff0c;日剧等海外影视资源&#xff0c;人人…

简单高效,分享几款我在使用的效率神器

做一个积极的人 编码、改bug、提升自己 我有一个乐园&#xff0c;面向编程&#xff0c;春暖花开&#xff01; 今天周六了&#xff0c;分享几款我目前在用的小工具&#xff0c;希望对你有用。使用工具的好处等等&#xff0c;我就不过多介绍了&#xff0c;下面文章的内容是先简单…

如何在免费追剧?Python制作视频解析免费追剧神器

前言 同学们在闲暇之余是否喜欢看电影或者电视剧呢&#xff1f; 今天带领大家使用python制作能免费追剧的桌面软件。还在等什么&#xff1f;发车了&#xff01; 效果我就不再这里演示了&#x1f612; Python从零基础入门到实战系统教程、源码、视频&#xff0c;想要数据集的…

有什么让你相见恨晚的 MacBook 神器?

给大家推荐一些都是免费且能极大地提高 Mac 使用体验的小工具吧。 快速启动工具&#xff1a;Manico 如果你和派君一样喜欢用键盘来切换应用&#xff0c;那么 Manico 就是你心目中的神器了。 Manico 的使用非常简单。安装打开后&#xff0c;按下 「Option」 键&#xff0c;屏…

手机端追剧神器,最新最火电影免费看,非常牛批!

哈喽&#xff0c;大家周末好呀&#xff0c;好久没给大家分享看电视剧的App了&#xff0c;今天给大家分享一个免VIP看电影电视剧的软件是一款影视神器&#xff0c;非常的好用&#xff0c;它就是 - 影迷天堂 软件介绍 这是一款资源非常丰富的影视神器&#xff0c;例如最近最新最火…

【Android工具】更新安卓TV云存储观影工具,安卓电视看剧看电影工具小结

微信关注公众号 “DLGG创客DIY” 设为“星标”&#xff0c;重磅干货&#xff0c;第一时间送达。 之前分享过&#xff1a;【Android工具】安卓TV云存储观影工具测试正常&#xff0c;安卓电视看电影方案小结 230214发现上次分享的小白云盘tv1.2没法看了&#xff0c;于是便上网找了…

被封杀4年的看片神器终于解禁了,要跟百度网盘抢生意?(末尾送书)

点击上方蓝色小字&#xff0c;关注“涛哥聊Python” 重磅干货&#xff0c;第一时间送达 说到看片工具&#xff0c;大家第一个想到的或许是快播。 自从快播没落以后&#xff0c;迅雷就成了广大宅男必备的下片神器&#xff0c;一直以来都广受人们喜爱。 对于习惯使用手机看片的小…

Alfred神器使用手册

点击上方“芋道源码”&#xff0c;选择“设为星标” 管她前浪&#xff0c;还是后浪&#xff1f; 能浪的浪&#xff0c;才是好浪&#xff01; 每天 10:33 更新文章&#xff0c;每天掉亿点点头发... 源码精品专栏 原创 | Java 2021 超神之路&#xff0c;很肝~中文详细注释的开源…

pptv手机端html,影视资源持续更新,PPTV手机化身看片神器

移动终端的快速发展为人们的生活带来了多元化的娱乐享受&#xff0c;用手机观赏电影逐渐成为人们放松身心的重要选择之一。虽然在手机品牌呈井喷发展的态势下&#xff0c;支持观影的智能手机不在少数&#xff0c;要想突出重围&#xff0c;仅仅在硬件配置上做文章是远远不够的&a…

牧云Webshell检测神器

最近一直在加班搞案例库&#xff0c;从接到需求到完成编写&#xff0c;只给了1个星期的时间。虽说是一个简单的活&#xff0c;但是也是费时费力&#xff0c;安全服务团队编写的文档&#xff0c;需要做大量的修改&#xff0c;才能成为产品中的一部分&#xff0c;各种截图还需要规…

python之爬虫神器selenium:猫眼电影榜单并进行数据可视化

如果你在学习爬虫&#xff0c;那么你一定爬取过豆瓣或猫眼电影的榜单&#xff0c;但大多数教程都是用的requests正则&#xff0c;但对于很多新手来讲&#xff0c;requests获取网页容易&#xff0c;但是用正则表达式解析网页就难的多了。那么&#xff0c;让我们告别看不懂&#…