回收站没有显示我的图像:-E / RecyclerView:未连接适配器;跳过布局

时间:2019-06-18 13:38:52

标签: java android recycler-adapter

我正在构建用于将带有名称的图片上传到Firebase的应用程序,然后将其与文本一起在Recycler视图中检索,我已经成功上传了图片,但遗憾的是,我无法在Recycler Viewer中查看它正在加载空图片(回收者试图加载图片,但未按照所附图片显示图片),调试器给出此错误:-E / RecyclerView:未连接适配器;跳过布局

请检查随附的图片和代码。

已更新,请检查新的调试代码 问候。

//display image actvivty //

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;

import java.util.ArrayList;
import java.util.List;

public class DisplayImagesActivity extends AppCompatActivity {

    // Creating DatabaseReference.
    DatabaseReference databaseReference;

    // Creating RecyclerView.
    RecyclerView recyclerView;

    // Creating RecyclerView.Adapter.
    RecyclerView.Adapter adapter ;

    // Creating Progress dialog
    ProgressDialog progressDialog;

    // Creating List of ImageUploadInfo class.
    List<ImageUploadInfo> list = new ArrayList<>();


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

        // Assign id to RecyclerView.
        recyclerView = (RecyclerView) findViewById(R.id.RecyclerView);

        // Setting RecyclerView size true.
        recyclerView.setHasFixedSize(true);

        // Setting RecyclerView layout as LinearLayout.
        recyclerView.setLayoutManager(new LinearLayoutManager(DisplayImagesActivity.this));

        // Assign activity this to progress dialog.
        progressDialog = new ProgressDialog(DisplayImagesActivity.this);

        // Setting up message in Progress dialog.
        progressDialog.setMessage("Loading Images From Firebase.");

        // Showing progress dialog.
        progressDialog.show();

        // Setting up Firebase image upload folder path in databaseReference.
        // The path is already defined in MainActivity.
        databaseReference = FirebaseDatabase.getInstance().getReference(UserProfileUpdaterActivity.Database_Path);

        // Adding Add Value Event Listener to databaseReference.
        databaseReference.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot snapshot) {

                for (DataSnapshot postSnapshot : snapshot.getChildren()) {

                    ImageUploadInfo imageUploadInfo = postSnapshot.getValue(ImageUploadInfo.class);

                    list.add(imageUploadInfo);
                }

                adapter = new RecyclerViewAdapter(getApplicationContext(), list);

                recyclerView.setAdapter(adapter);

                // Hiding the progress dialog.
                progressDialog.dismiss();
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

                // Hiding the progress dialog.
                progressDialog.dismiss();

            }
        });

    }
}

//user profile activity to upload and download data //

package com.example.boc;

import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.webkit.MimeTypeMap;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;

import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.storage.FirebaseStorage;
import com.google.firebase.storage.OnProgressListener;
import com.google.firebase.storage.StorageReference;
import com.google.firebase.storage.UploadTask;

import java.io.IOException;

public class UserProfileUpdaterActivity extends AppCompatActivity {

    // Folder path for Firebase Storage.
    String Storage_Path = "All_Image_Uploads/";

    // Root Database Name for Firebase Database.
    public static final String Database_Path = "All_Image_Uploads_Database";

    // Creating button.
    Button ChooseButton, UploadButton, DisplayImageButton;

    // Creating EditText.
    EditText ImageName ;

    // Creating ImageView.
    ImageView SelectImage;

    // Creating URI.
    Uri FilePathUri;

    // Creating StorageReference and DatabaseReference object.
    StorageReference storageReference;
    DatabaseReference databaseReference;

    // Image request code for onActivityResult() .
    int Image_Request_Code = 7;

    ProgressDialog progressDialog ;

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

        // Assign FirebaseStorage instance to storageReference.
        storageReference = FirebaseStorage.getInstance().getReference();

        // Assign FirebaseDatabase instance with root database name.
        databaseReference = FirebaseDatabase.getInstance().getReference(Database_Path);

        //Assign ID'S to button.
        ChooseButton = (Button)findViewById(R.id.ButtonChooseImage);
        UploadButton = (Button)findViewById(R.id.ButtonUploadImage);

        DisplayImageButton = (Button)findViewById(R.id.DisplayImagesButton);

        // Assign ID's to EditText.
        ImageName = (EditText)findViewById(R.id.ImageNameEditText);

        // Assign ID'S to image view.
        SelectImage = (ImageView)findViewById(R.id.ShowImageView);

        // Assigning Id to ProgressDialog.
        progressDialog = new ProgressDialog(UserProfileUpdaterActivity.this);

        // Adding click listener to Choose image button.
        ChooseButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                // Creating intent.
                Intent intent = new Intent();

                // Setting intent type as image to select image from phone storage.
                intent.setType("image/*");
                intent.setAction(Intent.ACTION_GET_CONTENT);
                startActivityForResult(Intent.createChooser(intent, "Please Select Image"), Image_Request_Code);

            }
        });


        // Adding click listener to Upload image button.
        UploadButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                // Calling method to upload selected image on Firebase storage.
                UploadImageFileToFirebaseStorage();

            }
        });


        DisplayImageButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                Intent intent = new Intent(UserProfileUpdaterActivity.this, DisplayImagesActivity.class);
                startActivity(intent);

            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == Image_Request_Code && resultCode == RESULT_OK && data != null && data.getData() != null) {

            FilePathUri = data.getData();

            try {

                // Getting selected image into Bitmap.
                Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), FilePathUri);

                // Setting up bitmap selected image into ImageView.
                SelectImage.setImageBitmap(bitmap);

                // After selecting image change choose button above text.
                ChooseButton.setText("Image Selected");

            }
            catch (IOException e) {

                e.printStackTrace();
            }
        }
    }

    // Creating Method to get the selected image file Extension from File Path URI.
    public String GetFileExtension(Uri uri) {

        ContentResolver contentResolver = getContentResolver();

        MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();

        // Returning the file Extension.
        return mimeTypeMap.getExtensionFromMimeType(contentResolver.getType(uri)) ;

    }

    // Creating UploadImageFileToFirebaseStorage method to upload image on storage.
    public void UploadImageFileToFirebaseStorage() {

        // Checking whether FilePathUri Is empty or not.
        if (FilePathUri != null) {

            // Setting progressDialog Title.
            progressDialog.setTitle("Image is Uploading...");

            // Showing progressDialog.
            progressDialog.show();

            // Creating second StorageReference.
            StorageReference storageReference2nd = storageReference.child(Storage_Path + System.currentTimeMillis() + "." + GetFileExtension(FilePathUri));

            // Adding addOnSuccessListener to second StorageReference.
            storageReference2nd.putFile(FilePathUri)
                    .addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
                        @Override
                        public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {

                            // Getting image name from EditText and store into string variable.
                            String TempImageName = ImageName.getText().toString().trim();

                            // Hiding the progressDialog after done uploading.
                            progressDialog.dismiss();

                            // Showing toast message after done uploading.
                            Toast.makeText(getApplicationContext(), "Image Uploaded Successfully ", Toast.LENGTH_LONG).show();

                            @SuppressWarnings("VisibleForTests")
                            ImageUploadInfo imageUploadInfo = new ImageUploadInfo(TempImageName, taskSnapshot.getStorage().getDownloadUrl().toString());

                            // Getting image upload ID.
                            String ImageUploadId = databaseReference.push().getKey();

                            // Adding image upload id s child element into databaseReference.
                            databaseReference.child(ImageUploadId).setValue(imageUploadInfo);
                        }
                    })
                    // If something goes wrong .
                    .addOnFailureListener(new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception exception) {

                            // Hiding the progressDialog.
                            progressDialog.dismiss();

                            // Showing exception erro message.
                            Toast.makeText(UserProfileUpdaterActivity.this, exception.getMessage(), Toast.LENGTH_LONG).show();
                        }
                    })

                    // On progress change upload time.
                    .addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() {
                        @Override
                        public void onProgress(UploadTask.TaskSnapshot taskSnapshot) {

                            // Setting progressDialog Title.
                            progressDialog.setTitle("Image is Uploading...");

                        }
                    });
        }
        else {

            Toast.makeText(UserProfileUpdaterActivity.this, "Please Select Image or Add Image Name", Toast.LENGTH_LONG).show();

        }
    }


}

//recycler view adapater //
package com.example.boc;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;

import java.util.List;

/**
 * Created by AndroidJSon.com on 6/18/2017.
 */

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {

    Context context;
    List<ImageUploadInfo> MainImageUploadInfoList;

    public RecyclerViewAdapter(Context context, List<ImageUploadInfo> TempList) {

        this.MainImageUploadInfoList = TempList;

        this.context = context;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_items, parent, false);

        ViewHolder viewHolder = new ViewHolder(view);

        return viewHolder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        ImageUploadInfo UploadInfo = MainImageUploadInfoList.get(position);

        holder.imageNameTextView.setText(UploadInfo.getImageName());

        //Loading image from Glide library.
        Glide.with(context).load(UploadInfo.getImageURL()).into(holder.imageView);
    }

    @Override
    public int getItemCount() {

        return MainImageUploadInfoList.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder {

        public ImageView imageView;
        public TextView imageNameTextView;

        public ViewHolder(View itemView) {
            super(itemView);

            imageView = (ImageView) itemView.findViewById(R.id.imageView);

            imageNameTextView = (TextView) itemView.findViewById(R.id.ImageNameTextView);
        }
    }
}

//image upload info activity //
package com.example.boc;
public class ImageUploadInfo {

    public String imageName;
    public String imageURL;
    public ImageUploadInfo() {
    }
    public ImageUploadInfo(String name, String url) {
        this.imageName = name;
        this.imageURL= url;
    }
    public String getImageName() {
        return imageName;
    }
    public String getImageURL() {
        return imageURL;
    }
}

//debugger output //
D/InputTransport: Input channel constructed: fd=81
    Input channel destroyed: fd=87
E/ViewRootImpl: sendUserActionEvent() mView == null
W/IInputConnectionWrapper: finishComposingText on inactive InputConnection
W/Glide: Load failed for com.google.android.gms.tasks.zzu@75e397d with size [1404x700]
    class com.bumptech.glide.load.engine.GlideException: Failed to load resource
    There were 3 causes:
    java.io.FileNotFoundException(/com.google.android.gms.tasks.zzu@75e397d (No such file or directory))
    java.io.FileNotFoundException(No such file or directory)
    java.io.FileNotFoundException(No such file or directory)
     call GlideException#logRootCauses(String) for more detail
      Cause (1 of 3): class com.bumptech.glide.load.engine.GlideException: Fetching data failed, class java.io.InputStream, LOCAL
    There was 1 cause:
    java.io.FileNotFoundException(/com.google.android.gms.tasks.zzu@75e397d (No such file or directory))
     call GlideException#logRootCauses(String) for more detail
        Cause (1 of 1): class com.bumptech.glide.load.engine.GlideException: Fetch failed
    There was 1 cause:
    java.io.FileNotFoundException(/com.google.android.gms.tasks.zzu@75e397d (No such file or directory))
     call GlideException#logRootCauses(String) for more detail
          Cause (1 of 1): class java.io.FileNotFoundException: /com.google.android.gms.tasks.zzu@75e397d (No such file or directory)
      Cause (2 of 3): class com.bumptech.glide.load.engine.GlideException: Fetching data failed, class android.os.ParcelFileDescriptor, LOCAL
    There was 1 cause:
    java.io.FileNotFoundException(No such file or directory)
     call GlideException#logRootCauses(String) for more detail
        Cause (1 of 1): class com.bumptech.glide.load.engine.GlideException: Fetch failed
    There was 1 cause:
    java.io.FileNotFoundException(No such file or directory)
     call GlideException#logRootCauses(String) for more detail
          Cause (1 of 1): class java.io.FileNotFoundException: No such file or directory
      Cause (3 of 3): class com.bumptech.glide.load.engine.GlideException: Fetching data failed, class android.content.res.AssetFileDescriptor, LOCAL
    There was 1 cause:
    java.io.FileNotFoundException(No such file or directory)
     call GlideException#logRootCauses(String) for more detail
        Cause (1 of 1): class java.io.FileNotFoundException: No such file or directory
I/Glide: Root cause (1 of 3)
    java.io.FileNotFoundException: /com.google.android.gms.tasks.zzu@75e397d (No such file or directory)
        at java.io.FileInputStream.open(Native Method)
        at java.io.FileInputStream.<init>(FileInputStream.java:146)
        at java.io.FileInputStream.<init>(FileInputStream.java:99)
        at android.content.ContentResolver.openInputStream(ContentResolver.java:706)
        at com.bumptech.glide.load.data.StreamLocalUriFetcher.loadResourceFromUri(StreamLocalUriFetcher.java:85)
        at com.bumptech.glide.load.data.StreamLocalUriFetcher.loadResource(StreamLocalUriFetcher.java:60)
        at com.bumptech.glide.load.data.StreamLocalUriFetcher.loadResource(StreamLocalUriFetcher.java:15)
        at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:44)
        at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.loadData(MultiModelLoader.java:99)
        at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:62)
        at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:302)
        at com.bumptech.glide.load.engine.DecodeJob.runWrapped(DecodeJob.java:272)
        at com.bumptech.glide.load.engine.DecodeJob.run(DecodeJob.java:233)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
        at java.lang.Thread.run(Thread.java:762)
        at com.bumptech.glide.load.engine.executor.GlideExecutor$DefaultThreadFactory$1.run(GlideExecutor.java:446)
I/Glide: Root cause (2 of 3)
    java.io.FileNotFoundException: No such file or directory
        at android.os.Parcel.openFileDescriptor(Native Method)
        at android.os.ParcelFileDescriptor.openInternal(ParcelFileDescriptor.java:283)
        at android.os.ParcelFileDescriptor.open(ParcelFileDescriptor.java:200)
        at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:983)
        at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:911)
        at com.bumptech.glide.load.data.FileDescriptorLocalUriFetcher.loadResource(FileDescriptorLocalUriFetcher.java:22)
        at com.bumptech.glide.load.data.FileDescriptorLocalUriFetcher.loadResource(FileDescriptorLocalUriFetcher.java:14)
        at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:44)
        at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.loadData(MultiModelLoader.java:99)
        at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:62)
        at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:302)
        at com.bumptech.glide.load.engine.DecodeJob.onDataFetcherFailed(DecodeJob.java:397)
        at com.bumptech.glide.load.engine.SourceGenerator.onLoadFailed(SourceGenerator.java:119)
        at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.startNextOrFail(MultiModelLoader.java:153)
        at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.onLoadFailed(MultiModelLoader.java:144)
        at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:49)
        at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.loadData(MultiModelLoader.java:99)
        at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:62)
        at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:302)
        at com.bumptech.glide.load.engine.DecodeJob.runWrapped(DecodeJob.java:272)
        at com.bumptech.glide.load.engine.DecodeJob.run(DecodeJob.java:233)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
        at java.lang.Thread.run(Thread.java:762)
        at com.bumptech.glide.load.engine.executor.GlideExecutor$DefaultThreadFactory$1.run(GlideExecutor.java:446)
I/Glide: Root cause (3 of 3)
    java.io.FileNotFoundException: No such file or directory
        at android.os.Parcel.openFileDescriptor(Native Method)
        at android.os.ParcelFileDescriptor.openInternal(ParcelFileDescriptor.java:283)
        at android.os.ParcelFileDescriptor.open(ParcelFileDescriptor.java:200)
        at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:983)
        at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:911)
        at com.bumptech.glide.load.data.AssetFileDescriptorLocalUriFetcher.loadResource(AssetFileDescriptorLocalUriFetcher.java:22)
        at com.bumptech.glide.load.data.AssetFileDescriptorLocalUriFetcher.loadResource(AssetFileDescriptorLocalUriFetcher.java:13)
        at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:44)
        at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:62)
        at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:302)
        at com.bumptech.glide.load.engine.DecodeJob.onDataFetcherFailed(DecodeJob.java:397)
        at com.bumptech.glide.load.engine.SourceGenerator.onLoadFailed(SourceGenerator.java:119)
        at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.startNextOrFail(MultiModelLoader.java:153)
        at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.onLoadFailed(MultiModelLoader.java:144)
        at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:49)
        at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.loadData(MultiModelLoader.java:99)
        at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:62)
        at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:302)
        at com.bumptech.glide.load.engine.DecodeJob.onDataFetcherFailed(DecodeJob.java:397)
        at com.bumptech.glide.load.engine.SourceGenerator.onLoadFailed(SourceGenerator.java:119)
        at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.startNextOrFail(MultiModelLoader.java:153)
        at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.onLoadFailed(MultiModelLoader.java:144)
        at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:49)
        at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.loadData(MultiModelLoader.java:99)
        at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:62)
        at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:302)
        at com.bumptech.glide.load.engine.DecodeJob.runWrapped(DecodeJob.java:272)
        at com.bumptech.glide.load.engine.DecodeJob.run(DecodeJob.java:233)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
        at java.lang.Thread.run(Thread.java:762)
        at com.bumptech.glide.load.engine.executor.GlideExecutor$DefaultThreadFactory$1.run(GlideExecutor.java:446)
W/Glide: Load failed for com.google.android.gms.tasks.zzu@39d6f51 with size [1404x700]
    class com.bumptech.glide.load.engine.GlideException: Failed to load resource
    There were 3 causes:
   

enter image description here我在互联网上进行搜索,但没有任何解决方法。

我希望有人可以为我更新代码,以便在该回收站视图中显示图像。

2 个答案:

答案 0 :(得分:1)

似乎您的onDataChange中的代码未执行。我将在内部添加一些日志记录,以确保项目是否真正到达内部。否则,您的recyclerView代码看起来还可以。

更新:

在调试器中记录图像URL。然后检查该URL。您的imageUrl可能是文件uri,而glide正在尝试在磁盘上查找文件,而不是下载图像

更新2:

ImageUploadInfo imageUploadInfo = postSnapshot.getValue(ImageUploadInfo.class);

此行未将imageUrl正确加载到对象。可能是将一个对象而不是字符串分配给您的imageUrl属性。这种类型就是您在 FileNotFoundException 消息中看到的任何类型。

答案 1 :(得分:0)

您应该为glide添加一个commentProcessor)

添加依赖项

annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'

在您的gradle文件中。

显示您的日志:

  

无法找到GeneratedAppGlideModule。您应该包括一个   commentProcessor的编译依赖   com.github.bumptech.glide:您的应用程序中的编译器和   @GlideModule注释的AppGlideModule实现或   LibraryGlideModules将被静默忽略