@Override 重写父类的方法. @Nullable 表示定义的字段可以为空.

本文介绍如何通过注解@Nullable来约束参数,从而有效减少代码中出现NullPointerExceptions的情况。通过具体示例展示了在Fragment中加载视图时如何应用此注解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

@Override 重写父类的方法.

@Nullable 表示定义的字段可以为空.

为了减少代码中的NullPointerExceptions,可以用@Nullable对参数进行约束: 其实并不是异常就不存在,只是把它放在统一的地方进行管理了。

public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    return inflater.inflate(R.layout.fragment_main, container, false);
}

/* * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.scheduling.quartz; import java.io.IOException; import java.util.Map; import java.util.Properties; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import javax.sql.DataSource; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SchedulerFactory; import org.quartz.impl.RemoteScheduler; import org.quartz.impl.SchedulerRepository; import org.quartz.impl.StdSchedulerFactory; import org.quartz.simpl.SimpleThreadPool; import org.quartz.spi.JobFactory; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.SmartLifecycle; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.lang.Nullable; import org.springframework.scheduling.SchedulingException; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; /** * {@link FactoryBean} that creates and configures a Quartz {@link org.quartz.Scheduler}, * manages its lifecycle as part of the Spring application context, and exposes the * Scheduler as bean reference for dependency injection. * * <p>Allows registration of JobDetails, Calendars and Triggers, automatically * starting the scheduler on initialization and shutting it down on destruction. * In scenarios that just require static registration of jobs at startup, there * is no need to access the Scheduler instance itself in application code. * * <p>For dynamic registration of jobs at runtime, use a bean reference to * this SchedulerFactoryBean to get direct access to the Quartz Scheduler * ({@code org.quartz.Scheduler}). This allows you to create new jobs * and triggers, and also to control and monitor the entire Scheduler. * * <p>Note that Quartz instantiates a new Job for each execution, in * contrast to Timer which uses a TimerTask instance that is shared * between repeated executions. Just JobDetail descriptors are shared. * * <p>When using persistent jobs, it is strongly recommended to perform all * operations on the Scheduler within Spring-managed (or plain JTA) transactions. * Else, database locking will not properly work and might even break. * (See {@link #setDataSource setDataSource} javadoc for details.) * * <p>The preferred way to achieve transactional execution is to demarcate * declarative transactions at the business facade level, which will * automatically apply to Scheduler operations performed within those scopes. * Alternatively, you may add transactional advice for the Scheduler itself. * * <p>Compatible with Quartz 2.1.4 and higher, as of Spring 4.1. * * @author Juergen Hoeller * @since 18.02.2004 * @see #setDataSource * @see org.quartz.Scheduler * @see org.quartz.SchedulerFactory * @see org.quartz.impl.StdSchedulerFactory * @see org.springframework.transaction.interceptor.TransactionProxyFactoryBean */ public class SchedulerFactoryBean extends SchedulerAccessor implements FactoryBean<Scheduler>, BeanNameAware, ApplicationContextAware, InitializingBean, DisposableBean, SmartLifecycle { /** * The thread count property. */ public static final String PROP_THREAD_COUNT = "org.quartz.threadPool.threadCount"; /** * The default thread count. */ public static final int DEFAULT_THREAD_COUNT = 10; private static final ThreadLocal<ResourceLoader> configTimeResourceLoaderHolder = new ThreadLocal<>(); private static final ThreadLocal<Executor> configTimeTaskExecutorHolder = new ThreadLocal<>(); private static final ThreadLocal<DataSource> configTimeDataSourceHolder = new ThreadLocal<>(); private static final ThreadLocal<DataSource> configTimeNonTransactionalDataSourceHolder = new ThreadLocal<>(); /** * Return the {@link ResourceLoader} for the currently configured Quartz Scheduler, * to be used by {@link ResourceLoaderClassLoadHelper}. * <p>This instance will be set before initialization of the corresponding Scheduler, * and reset immediately afterwards. It is thus only available during configuration. * @see #setApplicationContext * @see ResourceLoaderClassLoadHelper */ @Nullable public static ResourceLoader getConfigTimeResourceLoader() { return configTimeResourceLoaderHolder.get(); } /** * Return the {@link Executor} for the currently configured Quartz Scheduler, * to be used by {@link LocalTaskExecutorThreadPool}. * <p>This instance will be set before initialization of the corresponding Scheduler, * and reset immediately afterwards. It is thus only available during configuration. * @since 2.0 * @see #setTaskExecutor * @see LocalTaskExecutorThreadPool */ @Nullable public static Executor getConfigTimeTaskExecutor() { return configTimeTaskExecutorHolder.get(); } /** * Return the {@link DataSource} for the currently configured Quartz Scheduler, * to be used by {@link LocalDataSourceJobStore}. * <p>This instance will be set before initialization of the corresponding Scheduler, * and reset immediately afterwards. It is thus only available during configuration. * @since 1.1 * @see #setDataSource * @see LocalDataSourceJobStore */ @Nullable public static DataSource getConfigTimeDataSource() { return configTimeDataSourceHolder.get(); } /** * Return the non-transactional {@link DataSource} for the currently configured * Quartz Scheduler, to be used by {@link LocalDataSourceJobStore}. * <p>This instance will be set before initialization of the corresponding Scheduler, * and reset immediately afterwards. It is thus only available during configuration. * @since 1.1 * @see #setNonTransactionalDataSource * @see LocalDataSourceJobStore */ @Nullable public static DataSource getConfigTimeNonTransactionalDataSource() { return configTimeNonTransactionalDataSourceHolder.get(); } @Nullable private SchedulerFactory schedulerFactory; private Class<? extends SchedulerFactory> schedulerFactoryClass = StdSchedulerFactory.class; @Nullable private String schedulerName; @Nullable private Resource configLocation; @Nullable private Properties quartzProperties; @Nullable private Executor taskExecutor; @Nullable private DataSource dataSource; @Nullable private DataSource nonTransactionalDataSource; @Nullable private Map<String, ?> schedulerContextMap; @Nullable private String applicationContextSchedulerContextKey; @Nullable private JobFactory jobFactory; private boolean jobFactorySet = false; private boolean autoStartup = true; private int startupDelay = 0; private int phase = DEFAULT_PHASE; private boolean exposeSchedulerInRepository = false; private boolean waitForJobsToCompleteOnShutdown = false; @Nullable private String beanName; @Nullable private ApplicationContext applicationContext; @Nullable private Scheduler scheduler; /** * Set an external Quartz {@link SchedulerFactory} instance to use. * <p>Default is an internal {@link StdSchedulerFactory} instance. If this method is * called, it overrides any class specified through {@link #setSchedulerFactoryClass} * as well as any settings specified through {@link #setConfigLocation}, * {@link #setQuartzProperties}, {@link #setTaskExecutor} or {@link #setDataSource}. * <p><b>NOTE:</b> With an externally provided {@code SchedulerFactory} instance, * local settings such as {@link #setConfigLocation} or {@link #setQuartzProperties} * will be ignored here in {@code SchedulerFactoryBean}, expecting the external * {@code SchedulerFactory} instance to get initialized on its own. * @since 4.3.15 * @see #setSchedulerFactoryClass */ public void setSchedulerFactory(SchedulerFactory schedulerFactory) { this.schedulerFactory = schedulerFactory; } /** * Set the Quartz {@link SchedulerFactory} implementation to use. * <p>Default is the {@link StdSchedulerFactory} class, reading in the standard * {@code quartz.properties} from {@code quartz.jar}. For applying custom Quartz * properties, specify {@link #setConfigLocation "configLocation"} and/or * {@link #setQuartzProperties "quartzProperties"} etc on this local * {@code SchedulerFactoryBean} instance. * @see org.quartz.impl.StdSchedulerFactory * @see #setConfigLocation * @see #setQuartzProperties * @see #setTaskExecutor * @see #setDataSource */ public void setSchedulerFactoryClass(Class<? extends SchedulerFactory> schedulerFactoryClass) { this.schedulerFactoryClass = schedulerFactoryClass; } /** * Set the name of the Scheduler to create via the SchedulerFactory, as an * alternative to the {@code org.quartz.scheduler.instanceName} property. * <p>If not specified, the name will be taken from Quartz properties * ({@code org.quartz.scheduler.instanceName}), or from the declared * {@code SchedulerFactoryBean} bean name as a fallback. * @see #setBeanName * @see StdSchedulerFactory#PROP_SCHED_INSTANCE_NAME * @see org.quartz.SchedulerFactory#getScheduler() * @see org.quartz.SchedulerFactory#getScheduler(String) */ public void setSchedulerName(String schedulerName) { this.schedulerName = schedulerName; } /** * Set the location of the Quartz properties config file, for example * as classpath resource "classpath:quartz.properties". * <p>Note: Can be omitted when all necessary properties are specified * locally via this bean, or when relying on Quartz' default configuration. * @see #setQuartzProperties */ public void setConfigLocation(Resource configLocation) { this.configLocation = configLocation; } /** * Set Quartz properties, like "org.quartz.threadPool.class". * <p>Can be used to override values in a Quartz properties config file, * or to specify all necessary properties locally. * @see #setConfigLocation */ public void setQuartzProperties(Properties quartzProperties) { this.quartzProperties = quartzProperties; } /** * Set a Spring-managed {@link Executor} to use as Quartz backend. * Exposed as thread pool through the Quartz SPI. * <p>Can be used to assign a local JDK ThreadPoolExecutor or a CommonJ * WorkManager as Quartz backend, to avoid Quartz's manual thread creation. * <p>By default, a Quartz SimpleThreadPool will be used, configured through * the corresponding Quartz properties. * @since 2.0 * @see #setQuartzProperties * @see LocalTaskExecutorThreadPool * @see org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor * @see org.springframework.scheduling.concurrent.DefaultManagedTaskExecutor */ public void setTaskExecutor(Executor taskExecutor) { this.taskExecutor = taskExecutor; } /** * Set the default {@link DataSource} to be used by the Scheduler. * <p>Note: If this is set, the Quartz settings should not define * a job store "dataSource" to avoid meaningless double configuration. * Also, do not define a "org.quartz.jobStore.class" property at all. * (You may explicitly define Spring's {@link LocalDataSourceJobStore} * but that's the default when using this method anyway.) * <p>A Spring-specific subclass of Quartz' JobStoreCMT will be used. * It is therefore strongly recommended to perform all operations on * the Scheduler within Spring-managed (or plain JTA) transactions. * Else, database locking will not properly work and might even break * (for example, if trying to obtain a lock on Oracle without a transaction). * <p>Supports both transactional and non-transactional DataSource access. * With a non-XA DataSource and local Spring transactions, a single DataSource * argument is sufficient. In case of an XA DataSource and global JTA transactions, * SchedulerFactoryBean's "nonTransactionalDataSource" property should be set, * passing in a non-XA DataSource that will not participate in global transactions. * @since 1.1 * @see #setNonTransactionalDataSource * @see #setQuartzProperties * @see #setTransactionManager * @see LocalDataSourceJobStore */ public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } /** * Set the {@link DataSource} to be used <i>for non-transactional access</i>. * <p>This is only necessary if the default DataSource is an XA DataSource that will * always participate in transactions: A non-XA version of that DataSource should * be specified as "nonTransactionalDataSource" in such a scenario. * <p>This is not relevant with a local DataSource instance and Spring transactions. * Specifying a single default DataSource as "dataSource" is sufficient there. * @since 1.1 * @see #setDataSource * @see LocalDataSourceJobStore */ public void setNonTransactionalDataSource(DataSource nonTransactionalDataSource) { this.nonTransactionalDataSource = nonTransactionalDataSource; } /** * Register objects in the Scheduler context via a given Map. * These objects will be available to any Job that runs in this Scheduler. * <p>Note: When using persistent Jobs whose JobDetail will be kept in the * database, do not put Spring-managed beans or an ApplicationContext * reference into the JobDataMap but rather into the SchedulerContext. * @param schedulerContextAsMap a Map with String keys and any objects as * values (for example Spring-managed beans) * @see JobDetailFactoryBean#setJobDataAsMap */ public void setSchedulerContextAsMap(Map<String, ?> schedulerContextAsMap) { this.schedulerContextMap = schedulerContextAsMap; } /** * Set the key of an {@link ApplicationContext} reference to expose in the * SchedulerContext, for example "applicationContext". Default is none. * Only applicable when running in a Spring ApplicationContext. * <p>Note: When using persistent Jobs whose JobDetail will be kept in the * database, do not put an ApplicationContext reference into the JobDataMap * but rather into the SchedulerContext. * <p>In case of a QuartzJobBean, the reference will be applied to the Job * instance as bean property. An "applicationContext" attribute will * correspond to a "setApplicationContext" method in that scenario. * <p>Note that BeanFactory callback interfaces like ApplicationContextAware * are not automatically applied to Quartz Job instances, because Quartz * itself is responsible for the lifecycle of its Jobs. * @see JobDetailFactoryBean#setApplicationContextJobDataKey * @see org.springframework.context.ApplicationContext */ public void setApplicationContextSchedulerContextKey(String applicationContextSchedulerContextKey) { this.applicationContextSchedulerContextKey = applicationContextSchedulerContextKey; } /** * Set the Quartz {@link JobFactory} to use for this Scheduler. * <p>Default is Spring's {@link AdaptableJobFactory}, which supports * {@link java.lang.Runnable} objects as well as standard Quartz * {@link org.quartz.Job} instances. Note that this default only applies * to a <i>local</i> Scheduler, not to a RemoteScheduler (where setting * a custom JobFactory is not supported by Quartz). * <p>Specify an instance of Spring's {@link SpringBeanJobFactory} here * (typically as an inner bean definition) to automatically populate a job's * bean properties from the specified job data map and scheduler context. * @since 2.0 * @see AdaptableJobFactory * @see SpringBeanJobFactory */ public void setJobFactory(JobFactory jobFactory) { this.jobFactory = jobFactory; this.jobFactorySet = true; } /** * Set whether to automatically start the scheduler after initialization. * <p>Default is "true"; set this to "false" to allow for manual startup. */ public void setAutoStartup(boolean autoStartup) { this.autoStartup = autoStartup; } /** * Return whether this scheduler is configured for auto-startup. If "true", * the scheduler will start after the context is refreshed and after the * start delay, if any. */ @Override public boolean isAutoStartup() { return this.autoStartup; } /** * Specify the phase in which this scheduler should be started and stopped. * The startup order proceeds from lowest to highest, and the shutdown order * is the reverse of that. By default this value is {@code Integer.MAX_VALUE} * meaning that this scheduler starts as late as possible and stops as soon * as possible. * @since 3.0 */ public void setPhase(int phase) { this.phase = phase; } /** * Return the phase in which this scheduler will be started and stopped. */ @Override public int getPhase() { return this.phase; } /** * Set the number of seconds to wait after initialization before * starting the scheduler asynchronously. Default is 0, meaning * immediate synchronous startup on initialization of this bean. * <p>Setting this to 10 or 20 seconds makes sense if no jobs * should be run before the entire application has started up. */ public void setStartupDelay(int startupDelay) { this.startupDelay = startupDelay; } /** * Set whether to expose the Spring-managed {@link Scheduler} instance in the * Quartz {@link SchedulerRepository}. Default is "false", since the Spring-managed * Scheduler is usually exclusively intended for access within the Spring context. * <p>Switch this flag to "true" in order to expose the Scheduler globally. * This is not recommended unless you have an existing Spring application that * relies on this behavior. Note that such global exposure was the accidental * default in earlier Spring versions; this has been fixed as of Spring 2.5.6. */ public void setExposeSchedulerInRepository(boolean exposeSchedulerInRepository) { this.exposeSchedulerInRepository = exposeSchedulerInRepository; } /** * Set whether to wait for running jobs to complete on shutdown. * <p>Default is "false". Switch this to "true" if you prefer * fully completed jobs at the expense of a longer shutdown phase. * @see org.quartz.Scheduler#shutdown(boolean) */ public void setWaitForJobsToCompleteOnShutdown(boolean waitForJobsToCompleteOnShutdown) { this.waitForJobsToCompleteOnShutdown = waitForJobsToCompleteOnShutdown; } @Override public void setBeanName(String name) { this.beanName = name; } @Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } //--------------------------------------------------------------------- // Implementation of InitializingBean interface //--------------------------------------------------------------------- @Override public void afterPropertiesSet() throws Exception { if (this.dataSource == null && this.nonTransactionalDataSource != null) { this.dataSource = this.nonTransactionalDataSource; } if (this.applicationContext != null && this.resourceLoader == null) { this.resourceLoader = this.applicationContext; } // Initialize the Scheduler instance... this.scheduler = prepareScheduler(prepareSchedulerFactory()); try { registerListeners(); registerJobsAndTriggers(); } catch (Exception ex) { try { this.scheduler.shutdown(true); } catch (Exception ex2) { logger.debug("Scheduler shutdown exception after registration failure", ex2); } throw ex; } } /** * Create a SchedulerFactory if necessary and apply locally defined Quartz properties to it. * @return the initialized SchedulerFactory */ private SchedulerFactory prepareSchedulerFactory() throws SchedulerException, IOException { SchedulerFactory schedulerFactory = this.schedulerFactory; if (schedulerFactory == null) { // Create local SchedulerFactory instance (typically a StdSchedulerFactory) schedulerFactory = BeanUtils.instantiateClass(this.schedulerFactoryClass); if (schedulerFactory instanceof StdSchedulerFactory stdSchedulerFactory) { initSchedulerFactory(stdSchedulerFactory); } else if (this.configLocation != null || this.quartzProperties != null || this.taskExecutor != null || this.dataSource != null) { throw new IllegalArgumentException( "StdSchedulerFactory required for applying Quartz properties: " + schedulerFactory); } // Otherwise, no local settings to be applied via StdSchedulerFactory.initialize(Properties) } // Otherwise, assume that externally provided factory has been initialized with appropriate settings return schedulerFactory; } /** * Initialize the given SchedulerFactory, applying locally defined Quartz properties to it. * @param schedulerFactory the SchedulerFactory to initialize */ private void initSchedulerFactory(StdSchedulerFactory schedulerFactory) throws SchedulerException, IOException { Properties mergedProps = new Properties(); if (this.resourceLoader != null) { mergedProps.setProperty(StdSchedulerFactory.PROP_SCHED_CLASS_LOAD_HELPER_CLASS, ResourceLoaderClassLoadHelper.class.getName()); } if (this.taskExecutor != null) { mergedProps.setProperty(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, LocalTaskExecutorThreadPool.class.getName()); } else { // Set necessary default properties here, as Quartz will not apply // its default configuration when explicitly given properties. mergedProps.setProperty(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, SimpleThreadPool.class.getName()); mergedProps.setProperty(PROP_THREAD_COUNT, Integer.toString(DEFAULT_THREAD_COUNT)); } if (this.configLocation != null) { if (logger.isDebugEnabled()) { logger.debug("Loading Quartz config from [" + this.configLocation + "]"); } PropertiesLoaderUtils.fillProperties(mergedProps, this.configLocation); } CollectionUtils.mergePropertiesIntoMap(this.quartzProperties, mergedProps); if (this.dataSource != null) { mergedProps.putIfAbsent(StdSchedulerFactory.PROP_JOB_STORE_CLASS, LocalDataSourceJobStore.class.getName()); } // Determine scheduler name across local settings and Quartz properties... if (this.schedulerName != null) { mergedProps.setProperty(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, this.schedulerName); } else { String nameProp = mergedProps.getProperty(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME); if (nameProp != null) { this.schedulerName = nameProp; } else if (this.beanName != null) { mergedProps.setProperty(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, this.beanName); this.schedulerName = this.beanName; } } schedulerFactory.initialize(mergedProps); } private Scheduler prepareScheduler(SchedulerFactory schedulerFactory) throws SchedulerException { if (this.resourceLoader != null) { // Make given ResourceLoader available for SchedulerFactory configuration. configTimeResourceLoaderHolder.set(this.resourceLoader); } if (this.taskExecutor != null) { // Make given TaskExecutor available for SchedulerFactory configuration. configTimeTaskExecutorHolder.set(this.taskExecutor); } if (this.dataSource != null) { // Make given DataSource available for SchedulerFactory configuration. configTimeDataSourceHolder.set(this.dataSource); } if (this.nonTransactionalDataSource != null) { // Make given non-transactional DataSource available for SchedulerFactory configuration. configTimeNonTransactionalDataSourceHolder.set(this.nonTransactionalDataSource); } // Get Scheduler instance from SchedulerFactory. try { Scheduler scheduler = createScheduler(schedulerFactory, this.schedulerName); populateSchedulerContext(scheduler); if (!this.jobFactorySet && !(scheduler instanceof RemoteScheduler)) { // Use AdaptableJobFactory as default for a local Scheduler, unless when // explicitly given a null value through the "jobFactory" bean property. this.jobFactory = new AdaptableJobFactory(); } if (this.jobFactory != null) { if (this.applicationContext != null && this.jobFactory instanceof ApplicationContextAware applicationContextAware) { applicationContextAware.setApplicationContext(this.applicationContext); } if (this.jobFactory instanceof SchedulerContextAware schedulerContextAware) { schedulerContextAware.setSchedulerContext(scheduler.getContext()); } scheduler.setJobFactory(this.jobFactory); } return scheduler; } finally { if (this.resourceLoader != null) { configTimeResourceLoaderHolder.remove(); } if (this.taskExecutor != null) { configTimeTaskExecutorHolder.remove(); } if (this.dataSource != null) { configTimeDataSourceHolder.remove(); } if (this.nonTransactionalDataSource != null) { configTimeNonTransactionalDataSourceHolder.remove(); } } } /** * Create the Scheduler instance for the given factory and scheduler name. * Called by {@link #afterPropertiesSet}. * <p>The default implementation invokes SchedulerFactory's {@code getScheduler} * method. Can be overridden for custom Scheduler creation. * @param schedulerFactory the factory to create the Scheduler with * @param schedulerName the name of the scheduler to create * @return the Scheduler instance * @throws SchedulerException if thrown by Quartz methods * @see #afterPropertiesSet * @see org.quartz.SchedulerFactory#getScheduler */ @SuppressWarnings("NullAway") protected Scheduler createScheduler(SchedulerFactory schedulerFactory, @Nullable String schedulerName) throws SchedulerException { // Override thread context ClassLoader to work around naive Quartz ClassLoadHelper loading. Thread currentThread = Thread.currentThread(); ClassLoader threadContextClassLoader = currentThread.getContextClassLoader(); boolean overrideClassLoader = (this.resourceLoader != null && this.resourceLoader.getClassLoader() != threadContextClassLoader); if (overrideClassLoader) { currentThread.setContextClassLoader(this.resourceLoader.getClassLoader()); } try { SchedulerRepository repository = SchedulerRepository.getInstance(); synchronized (repository) { Scheduler existingScheduler = (schedulerName != null ? repository.lookup(schedulerName) : null); Scheduler newScheduler = schedulerFactory.getScheduler(); if (newScheduler == existingScheduler) { throw new IllegalStateException("Active Scheduler of name '" + schedulerName + "' already registered " + "in Quartz SchedulerRepository. Cannot create a new Spring-managed Scheduler of the same name!"); } if (!this.exposeSchedulerInRepository) { // Need to remove it in this case, since Quartz shares the Scheduler instance by default! SchedulerRepository.getInstance().remove(newScheduler.getSchedulerName()); } return newScheduler; } } finally { if (overrideClassLoader) { // Reset original thread context ClassLoader. currentThread.setContextClassLoader(threadContextClassLoader); } } } /** * Expose the specified context attributes and/or the current * ApplicationContext in the Quartz SchedulerContext. */ private void populateSchedulerContext(Scheduler scheduler) throws SchedulerException { // Put specified objects into Scheduler context. if (this.schedulerContextMap != null) { scheduler.getContext().putAll(this.schedulerContextMap); } // Register ApplicationContext in Scheduler context. if (this.applicationContextSchedulerContextKey != null) { if (this.applicationContext == null) { throw new IllegalStateException( "SchedulerFactoryBean needs to be set up in an ApplicationContext " + "to be able to handle an 'applicationContextSchedulerContextKey'"); } scheduler.getContext().put(this.applicationContextSchedulerContextKey, this.applicationContext); } } /** * Start the Quartz Scheduler, respecting the "startupDelay" setting. * @param scheduler the Scheduler to start * @param startupDelay the number of seconds to wait before starting * the Scheduler asynchronously */ protected void startScheduler(final Scheduler scheduler, final int startupDelay) throws SchedulerException { if (startupDelay <= 0) { logger.info("Starting Quartz Scheduler now"); scheduler.start(); } else { if (logger.isInfoEnabled()) { logger.info("Will start Quartz Scheduler [" + scheduler.getSchedulerName() + "] in " + startupDelay + " seconds"); } // Not using the Quartz startDelayed method since we explicitly want a daemon // thread here, not keeping the JVM alive in case of all other threads ending. Thread schedulerThread = new Thread(() -> { try { TimeUnit.SECONDS.sleep(startupDelay); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); // simply proceed } if (logger.isInfoEnabled()) { logger.info("Starting Quartz Scheduler now, after delay of " + startupDelay + " seconds"); } try { scheduler.start(); } catch (SchedulerException ex) { throw new SchedulingException("Could not start Quartz Scheduler after delay", ex); } }); schedulerThread.setName("Quartz Scheduler [" + scheduler.getSchedulerName() + "]"); schedulerThread.setDaemon(true); schedulerThread.start(); } } //--------------------------------------------------------------------- // Implementation of FactoryBean interface //--------------------------------------------------------------------- @Override public Scheduler getScheduler() { Assert.state(this.scheduler != null, "No Scheduler set"); return this.scheduler; } @Override @Nullable public Scheduler getObject() { return this.scheduler; } @Override public Class<? extends Scheduler> getObjectType() { return (this.scheduler != null ? this.scheduler.getClass() : Scheduler.class); } @Override public boolean isSingleton() { return true; } //--------------------------------------------------------------------- // Implementation of SmartLifecycle interface //--------------------------------------------------------------------- @Override public void start() throws SchedulingException { if (this.scheduler != null) { try { startScheduler(this.scheduler, this.startupDelay); } catch (SchedulerException ex) { throw new SchedulingException("Could not start Quartz Scheduler", ex); } } } @Override public void stop() throws SchedulingException { if (this.scheduler != null) { try { this.scheduler.standby(); } catch (SchedulerException ex) { throw new SchedulingException("Could not stop Quartz Scheduler", ex); } } } @Override public boolean isRunning() throws SchedulingException { if (this.scheduler != null) { try { return !this.scheduler.isInStandbyMode(); } catch (SchedulerException ex) { return false; } } return false; } //--------------------------------------------------------------------- // Implementation of DisposableBean interface //--------------------------------------------------------------------- /** * Shut down the Quartz scheduler on bean factory shutdown, * stopping all scheduled jobs. */ @Override public void destroy() throws SchedulerException { if (this.scheduler != null) { logger.info("Shutting down Quartz Scheduler"); this.scheduler.shutdown(this.waitForJobsToCompleteOnShutdown); } } } 我看到的start和你的不一样
最新发布
07-26
package com.example.kucun2.function; import android.content.Context; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.Filter; import android.widget.Filterable; import android.widget.Spinner; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.example.kucun2.entity.Bancai; import com.example.kucun2.entity.Caizhi; import com.example.kucun2.entity.Chanpin; import com.example.kucun2.entity.Dingdan; import com.example.kucun2.entity.Mupi; import com.example.kucun2.entity.Zujian; import java.util.ArrayList; import java.util.List; public class Adapter { // 安全适配器基类,处理公共逻辑 // 安全适配器基类,处理公共逻辑 private static abstract class SafeSpinnerAdapter<T> extends ArrayAdapter<T> { private static final String TAG = "SafeSpinnerAdapter"; private final LayoutInflater inflater; private final String defaultText; public SafeSpinnerAdapter(Context context, int resource, List<T> objects, String defaultText) { super(context, resource, objects); this.inflater = LayoutInflater.from(context); this.defaultText = defaultText != null ? defaultText : "请选择"; } @Override public int getCount() { return super.getCount() + 1; // 增加一个默认项 } @Override public T getItem(int position) { return position == 0 ? null : super.getItem(position - 1); } @Override public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { if (parent == null) { Log.w(TAG, "Parent is null in getDropDownView"); return new TextView(getContext()); } if (convertView == null) { convertView = inflater.inflate(android.R.layout.simple_spinner_dropdown_item, parent, false); } TextView textView = convertView.findViewById(android.R.id.text1); if (position == 0) { textView.setText(defaultText); } else { T item = getItem(position); textView.setText(item != null ? formatDropdownText(item) : ""); } return convertView; } @NonNull @Override public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { if (parent == null) { Log.w(TAG, "Parent is null in getView"); return new TextView(getContext()); } if (convertView == null) { convertView = inflater.inflate(android.R.layout.simple_spinner_item, parent, false); } TextView textView = convertView.findViewById(android.R.id.text1); if (position == 0) { textView.setText(defaultText); } else { T item = getItem(position); textView.setText(item != null ? formatDisplayText(item) : ""); } return convertView; } protected abstract String formatDisplayText(T item); protected abstract String formatDropdownText(T item); } // 1. 材质适配器 public static ArrayAdapter<Caizhi> setupCaizhiSpinner(Spinner spinner, List<Caizhi> data, Context context) { ArrayAdapter<Caizhi> adapter = new SafeSpinnerAdapter<Caizhi>( context, android.R.layout.simple_spinner_item, data, "请选择材质" ) { @Override protected String formatDisplayText(Caizhi item) { return item != null ? item.getName() : ""; } @Override protected String formatDropdownText(Caizhi item) { return item != null ? item.getName() : ""; } }; adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(adapter); spinner.setSelection(0); // 默认选中"请选择" return adapter; } // 2. 木皮适配器 public static void setupMupiSpinner(Spinner spinner, List<Mupi> data, Context context) { ArrayAdapter<Mupi> adapter = new SafeSpinnerAdapter<Mupi>( context, android.R.layout.simple_spinner_item, data, "请选择木皮" ) { @Override protected String formatDisplayText(Mupi item) { return item != null ? item.formatMupiDisplay() : ""; } @Override protected String formatDropdownText(Mupi item) { return item != null ? item.formatMupiDisplay() : ""; } }; adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(adapter); spinner.setSelection(0); // 默认选中"请选择" } // 3. 板材适配器 public static void setupBancaiSpinners(Spinner spinner, List<Bancai> data, Context context) { ArrayAdapter<Bancai> adapter = new SafeSpinnerAdapter<Bancai>( context, android.R.layout.simple_spinner_item, data, "请选择板材" ) { @Override protected String formatDisplayText(Bancai item) { return item != null ? item.TableText() : ""; } @Override protected String formatDropdownText(Bancai item) { return item != null ? item.TableText() : ""; } }; adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(adapter); spinner.setSelection(0); // 默认选中"请选择" } // 4. 订单适配器 public static void setupDingdanSpinner(Spinner spinner, List<Dingdan> data, Context context) { ArrayAdapter<Dingdan> adapter = new SafeSpinnerAdapter<Dingdan>( context, android.R.layout.simple_spinner_item, data, "请选择订单" ) { @Override protected String formatDisplayText(Dingdan item) { return item != null ? item.getNumber() : ""; } @Override protected String formatDropdownText(Dingdan item) { return item != null ? item.getNumber() : ""; } }; adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(adapter); spinner.setSelection(0); // 默认选中"请选择" } // 5. 产品适配器 public static void setupChanpinSpinner(Spinner spinner, List<Chanpin> data, Context context) { ArrayAdapter<Chanpin> adapter = new SafeSpinnerAdapter<Chanpin>( context, android.R.layout.simple_spinner_item, data, "请选择产品" ) { @Override protected String formatDisplayText(Chanpin item) { return item != null ? item.getBianhao() : ""; } @Override protected String formatDropdownText(Chanpin item) { return item != null ? item.getBianhao() : ""; } }; adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(adapter); spinner.setSelection(0); // 默认选中"请选择" } // 6. 组件适配器 public static void setupZujianSpinner(Spinner spinner, List<Zujian> data, Context context) { ArrayAdapter<Zujian> adapter = new SafeSpinnerAdapter<Zujian>( context, android.R.layout.simple_spinner_item, data, "请选择组件" ) { @Override protected String formatDisplayText(Zujian item) { return item != null ? item.getName() : ""; } @Override protected String formatDropdownText(Zujian item) { return item != null ? item.getName() : ""; } }; adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(adapter); spinner.setSelection(0); // 默认选中"请选择" } // 1. 新增支持筛选的适配器基类 /** * 新增支持筛选的适配器基类 * @param <T> */ public static abstract class FilterableAdapter<T> extends ArrayAdapter<T> implements Filterable { private static final String TAG = "FilterableAdapter"; private final LayoutInflater inflater; private List<T> originalList; private List<T> filteredList; private final String defaultText; private final ItemFilter filter = new ItemFilter(); public FilterableAdapter(Context context, int resource, List<T> objects, String defaultText) { super(context, resource, objects); this.inflater = LayoutInflater.from(context); this.originalList = new ArrayList<>(objects); this.filteredList = new ArrayList<>(objects); this.defaultText = defaultText != null ? defaultText : "请选择"; } public FilterableAdapter(Context context, int resource, List<T> objects) { super(context, resource, objects); this.inflater = LayoutInflater.from(context); this.originalList = new ArrayList<>(objects); this.filteredList = new ArrayList<>(objects); this.defaultText = "请选择"; } @Override public int getCount() { return filteredList.size() + 1; // 增加默认项 } @Override public T getItem(int position) { return position == 0 ? null : filteredList.get(position - 1); // 位置偏移处理 } @NonNull @Override public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { if (convertView == null) { convertView = inflater.inflate(android.R.layout.simple_spinner_item, parent, false); } TextView textView = convertView.findViewById(android.R.id.text1); if (position == 0) { textView.setText(defaultText); // 默认提示文本 } else { T item = getItem(position); textView.setText(item != null ? formatDisplayText(item) : ""); } return convertView; } @Override public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { if (convertView == null) { convertView = inflater.inflate(android.R.layout.simple_spinner_dropdown_item, parent, false); } TextView textView = convertView.findViewById(android.R.id.text1); if (position == 0) { textView.setText(defaultText); // 下拉框中的默认提示 } else { T item = getItem(position); textView.setText(item != null ? formatDropdownText(item) : ""); } return convertView; } public void updateList(List<T> newList) { this.originalList = new ArrayList<>(newList); this.filteredList = new ArrayList<>(newList); notifyDataSetChanged(); } @Override public Filter getFilter() { return filter; } // 必须实现的抽象方法 protected abstract String formatDisplayText(T item); protected abstract String formatDropdownText(T item); private class ItemFilter extends Filter { @Override protected FilterResults performFiltering(CharSequence constraint) { FilterResults results = new FilterResults(); List<T> filtered = new ArrayList<>(); if (constraint == null || constraint.length() == 0) { // 无筛选条件时包含所有项 filtered.addAll(originalList); } else { String filterPattern = constraint.toString().toLowerCase().trim(); for (T item : originalList) { if (formatDisplayText(item).toLowerCase().contains(filterPattern)) { filtered.add(item); } } } results.values = filtered; results.count = filtered.size(); return results; } @Override @SuppressWarnings("unchecked") protected void publishResults(CharSequence constraint, FilterResults results) { filteredList = (List<T>) results.values; if (results.count > 0 || constraint == null || constraint.length() == 0) { notifyDataSetChanged(); } else { notifyDataSetInvalidated(); } } } } // /** * 为订单创建筛选适配器 * @param context * @param data * @return */ public static FilterableAdapter<Dingdan> createDingdanFilterableAdapter( Context context, List<Dingdan> data) { return new FilterableAdapter<Dingdan>(context, android.R.layout.simple_spinner_item, data) { @Override protected String formatDisplayText(Dingdan item) { return item != null ? item.getNumber() : ""; } @Override protected String formatDropdownText(Dingdan item) { return item != null ? item.getNumber() : ""; } }; } // 为产品创建筛选适配器 public static FilterableAdapter<Chanpin> createChanpinFilterableAdapter( Context context, List<Chanpin> data) { return new FilterableAdapter<Chanpin>(context, android.R.layout.simple_spinner_item, data) { @Override protected String formatDisplayText(Chanpin item) { return item != null ? item.getBianhao() : ""; } @Override protected String formatDropdownText(Chanpin item) { return item != null ? item.getBianhao() : ""; } }; } // 为组件创建筛选适配器 public static FilterableAdapter<Zujian> createZujianFilterableAdapter( Context context, List<Zujian> data) { return new FilterableAdapter<Zujian>(context, android.R.layout.simple_spinner_item, data) { @Override protected String formatDisplayText(Zujian item) { return item != null ? item.getName() : ""; } @Override protected String formatDropdownText(Zujian item) { return item != null ? item.getName() : ""; } }; } // 为板材创建筛选适配器 public static FilterableAdapter<Bancai> createBancaiFilterableAdapter( Context context, List<Bancai> data) { return new FilterableAdapter<Bancai>(context, android.R.layout.simple_spinner_item, data) { @Override protected String formatDisplayText(Bancai item) { return item != null ? item.TableText() : ""; } @Override protected String formatDropdownText(Bancai item) { return item != null ? item.TableText() : ""; } }; } } package com.example.kucun2.ui.jinhuo; import android.app.AlertDialog; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AutoCompleteTextView; import android.widget.Button; import android.widget.EditText; import android.widget.RadioGroup; import android.widget.Spinner; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import com.example.kucun2.DataPreserver.Data; import com.example.kucun2.R; import com.example.kucun2.entity.*; import com.example.kucun2.function.Adapter; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Objects; public class AddInventoryFragment extends Fragment implements Data.OnDataChangeListener { // 视图组件 private AutoCompleteTextView actvDingdan, actvChanpin, actvZujian, actvBancai; private EditText etQuantity; private RadioGroup rgType; private Button btnNewDingdan, btnAddChanpin, btnAddZujian, btnSubmit; // 适配器 private Adapter.FilterableAdapter<Dingdan> dingdanAdapter; private Adapter.FilterableAdapter<Chanpin> chanpinAdapter; private Adapter.FilterableAdapter<Zujian> zujianAdapter; private Adapter.FilterableAdapter<Bancai> bancaiAdapter; // 当前选择 private Dingdan selectedDingdan; private Chanpin selectedChanpin; private Zujian selectedZujian; private Bancai selectedBancai; // 数据列表 private List<Dingdan> dingdanList; private List<Chanpin> chanpinList; private List<Zujian> zujianList; private List<Bancai> bancaiList; // 当前用户 private User currentUser; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 获取当前用户 currentUser = Data.getCurrentUser(); } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_add_inventory, container, false); initViews(view); initData(); setupSpinners(); setupListeners(); applyPermissionRestrictions(); // 应用权限限制 return view; } /** * 初始化视图组件 */ private void initViews(View view) { actvDingdan = view.findViewById(R.id.actv_dingdan); actvChanpin = view.findViewById(R.id.actv_chanpin); actvZujian = view.findViewById(R.id.actv_zujian); actvBancai = view.findViewById(R.id.actv_bancai); etQuantity = view.findViewById(R.id.et_shuliang); rgType = view.findViewById(R.id.rg_type); btnNewDingdan = view.findViewById(R.id.btn_new_dingdan); btnAddChanpin = view.findViewById(R.id.btn_add_chanpin); btnAddZujian = view.findViewById(R.id.btn_add_zujian); btnSubmit = view.findViewById(R.id.btn_submit); // 初始禁用状态 actvChanpin.setEnabled(false); actvZujian.setEnabled(false); etQuantity.setEnabled(false); } private void initData() { // 从全局数据获取列表 dingdanList = Data.dingdans().getViewList(); chanpinList = Data.chanpins().getViewList(); zujianList = Data.zujians().getViewList(); bancaiList = Data.bancais().getViewList(); } /** * 设置下拉框适配器 */ private void setupSpinners() { // 3. 创建支持筛选的适配器 dingdanAdapter = Adapter.createDingdanFilterableAdapter(requireContext(), dingdanList); actvDingdan.setAdapter(dingdanAdapter); chanpinAdapter = Adapter.createChanpinFilterableAdapter(requireContext(), new ArrayList<>()); actvChanpin.setAdapter(chanpinAdapter); zujianAdapter = Adapter.createZujianFilterableAdapter(requireContext(), new ArrayList<>()); actvZujian.setAdapter(zujianAdapter); bancaiAdapter = Adapter.createBancaiFilterableAdapter(requireContext(), bancaiList); actvBancai.setAdapter(bancaiAdapter); } /** * 设置事件监听器 */ private void setupListeners() { // 4. 设置新的点击事件监听器 actvDingdan.setOnItemClickListener((parent, view, position, id) -> { selectedDingdan = dingdanAdapter.getItem(position); updateChanpinSpinner(); actvChanpin.setEnabled(selectedDingdan != null); if (selectedDingdan == null) { // 清后续选择 actvChanpin.setText(""); selectedChanpin = null; actvZujian.setText(""); selectedZujian = null; actvBancai.setText(""); selectedBancai = null; etQuantity.setText(""); etQuantity.setEnabled(false); } }); // 产品选择监听 actvChanpin.setOnItemClickListener((parent, view, position, id) -> { selectedChanpin = chanpinAdapter.getItem(position); updateZujianSpinner(); actvZujian.setEnabled(selectedChanpin != null); if (selectedChanpin == null) { // 清后续选择 actvZujian.setText(""); selectedZujian = null; actvBancai.setText(""); selectedBancai = null; etQuantity.setText(""); etQuantity.setEnabled(false); } }); // 组件选择监听 actvZujian.setOnItemClickListener((parent, view, position, id) -> { selectedZujian = zujianAdapter.getItem(position); updateBancaiSpinner(); // 组件选择后锁定板材下拉框 actvBancai.setEnabled(false); }); // 板材选择监听 actvBancai.setOnItemClickListener((parent, view, position, id) -> { selectedBancai = bancaiAdapter.getItem(position); etQuantity.setEnabled(selectedBancai != null); if (selectedBancai == null) { etQuantity.setText(""); } }); // 新建订单 btnNewDingdan.setOnClickListener(v -> showNewDingdanDialog()); // 添加产品 btnAddChanpin.setOnClickListener(v -> showAddChanpinDialog()); // 添加组件 btnAddZujian.setOnClickListener(v -> showAddZujianDialog()); // 提交 btnSubmit.setOnClickListener(v -> submitInventory()); } /** * 根据用户角色应用权限限制 */ private void applyPermissionRestrictions() { if (currentUser == null) return; int role = currentUser.getRole(); if (role == 0) { // 普通用户 // 只能消耗,不能进货 rgType.check(R.id.rb_xiaohao); rgType.getChildAt(0).setEnabled(false); // 禁用进货选项 // 禁用新建订单、添加产品按钮 btnNewDingdan.setEnabled(false); btnAddChanpin.setEnabled(false); } } /** * 根据选定订单更新产品下拉框 */ private void updateChanpinSpinner() { List<Chanpin> filtered = new ArrayList<>(); if (selectedDingdan != null) { for (Dingdan_chanpin dc : selectedDingdan.getDingdan_chanpin()) { filtered.add(dc.getChanpin()); } } // 5. 使用适配器的updateList方法更新数据 chanpinAdapter.updateList(filtered); } /** * 根据选定产品更新组件下拉框 */ private void updateZujianSpinner() { List<Zujian> filtered = new ArrayList<>(); if (selectedChanpin != null) { for (Chanpin_Zujian cz : selectedChanpin.getChanpin_zujian()) { filtered.add(cz.getZujian()); } } zujianAdapter.updateList(filtered); } /** * 根据选定组件更新板材下拉框 */ private void updateBancaiSpinner() { List<Bancai> filtered = new ArrayList<>(); if (selectedZujian != null && selectedChanpin != null) { // 查找组件关联的板材 for (Chanpin_Zujian cz : selectedChanpin.getChanpin_zujian()) { if (cz.getZujian().equals(selectedZujian)) { filtered.add(cz.getBancai()); // 自动选中关联的板材 selectedBancai = cz.getBancai(); actvBancai.setText(selectedBancai.TableText()); etQuantity.setEnabled(true); break; } } bancaiAdapter.updateList(filtered); } else { // 没有选择组件时显示所有板材 filtered = new ArrayList<>(bancaiList); bancaiAdapter.updateList(filtered); } } /** * 显示新建订单对话框 */ private void showNewDingdanDialog() { // 权限检查 if (currentUser != null && currentUser.getRole() == 0) { Toast.makeText(requireContext(), "您无权创建新订单", Toast.LENGTH_SHORT).show(); return; } AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); View view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_new_dingdan, null); EditText etNumber = view.findViewById(R.id.et_order_number); builder.setView(view) .setTitle("新建订单") .setPositiveButton("保存", (dialog, which) -> { Dingdan newDingdan = new Dingdan(); newDingdan.setNumber(etNumber.getText().toString()); // 添加到全局数据 Data.add(newDingdan); }) .setNegativeButton("取消", null) .show(); } /** * 显示添加产品对话框 */ private void showAddChanpinDialog() { // 权限检查 if (currentUser != null && currentUser.getRole() == 0) { Toast.makeText(requireContext(), "您无权添加产品", Toast.LENGTH_SHORT).show(); return; } if (selectedDingdan == null) { Toast.makeText(requireContext(), "请先选择订单", Toast.LENGTH_SHORT).show(); return; } AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); View view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_add_chanpin, null); Spinner spinner = view.findViewById(R.id.spinner_chanpin_selection); EditText etQuantity = view.findViewById(R.id.et_chanpin_quantity); // 设置产品列表(排除已关联的) List<Chanpin> available = new ArrayList<>(chanpinList); for (Dingdan_chanpin dc : selectedDingdan.getDingdan_chanpin()) { available.remove(dc.getChanpin()); } Adapter.setupChanpinSpinner(spinner, available, requireContext()); // 添加新建产品按钮 Button btnNewChanpin = view.findViewById(R.id.btn_new_chanpin); builder.setView(view) .setTitle("添加产品到订单") .setPositiveButton("添加", (dialog, which) -> { Chanpin selected = (Chanpin) spinner.getSelectedItem(); int quantity = Integer.parseInt(etQuantity.getText().toString().trim()); // 检查是否已存在关联 for (Dingdan_chanpin dc : selectedDingdan.getDingdan_chanpin()) { if (dc.getChanpin().equals(selected)) { Toast.makeText(requireContext(), "该产品已添加到订单", Toast.LENGTH_SHORT).show(); return; } } // 创建订单-产品关联 Dingdan_chanpin dc = new Dingdan_chanpin(); dc.setDingdan(selectedDingdan); dc.setChanpin(selected); dc.setShuliang(quantity); // 添加到全局数据 Data.add(dc); selectedDingdan.getDingdan_chanpin().add(dc); }) .show(); // 新建产品按钮点击事件 btnNewChanpin.setOnClickListener(v -> showNewChanpinDialog(available, spinner)); } // 实现新建产品对话框 private void showNewChanpinDialog(List<Chanpin> available, Spinner spinner) { AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); View view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_new_chanpin, null); EditText etBianhao = view.findViewById(R.id.et_chanpin_name); builder.setView(view) .setTitle("新建产品") .setPositiveButton("保存", (dialog, which) -> { String bianhao = etBianhao.getText().toString().trim(); if (bianhao.isEmpty()) { Toast.makeText(requireContext(), "产品编号不能为", Toast.LENGTH_SHORT).show(); return; } // 创建新产品 Chanpin newChanpin = new Chanpin(); newChanpin.setBianhao(bianhao); // 添加到全局数据 Data.add(newChanpin); // 更新可用列表和适配器 available.add(newChanpin); }) .setNegativeButton("取消", null) .show(); } /** * 显示添加组件对话框 */ private void showAddZujianDialog() { if (selectedChanpin == null) { Toast.makeText(requireContext(), "请先选择产品", Toast.LENGTH_SHORT).show(); return; } AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); View view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_create_zujian_bancai, null); Spinner spinnerZujian = view.findViewById(R.id.et_zujian_name); Spinner spinnerBancai = view.findViewById(R.id.spinner_bancai); EditText etOneHowmany = view.findViewById(R.id.number_one_howmany); // 设置组件下拉框 Adapter.setupZujianSpinner(spinnerZujian, zujianList, requireContext()); // 设置板材下拉框 Adapter.setupBancaiSpinners(spinnerBancai, bancaiList, requireContext()); builder.setView(view) .setTitle("添加组件到产品") .setPositiveButton("添加", (dialog, which) -> { Zujian zujian = (Zujian) spinnerZujian.getSelectedItem(); Bancai bancai = (Bancai) spinnerBancai.getSelectedItem(); double oneHowmany = Double.parseDouble(etOneHowmany.getText().toString()); // 检查是否已存在关联 for (Chanpin_Zujian cz : selectedChanpin.getChanpin_zujian()) { if (cz.getZujian().equals(zujian) && cz.getBancai().equals(bancai)) { Toast.makeText(requireContext(), "该组件已添加到产品", Toast.LENGTH_SHORT).show(); return; } } // 创建产品-组件关联 Chanpin_Zujian cz = new Chanpin_Zujian(); cz.setChanpin(selectedChanpin); cz.setZujian(zujian); cz.setBancai(bancai); cz.setOne_howmany(oneHowmany); zujian.getChanpin_zujian().add(cz); selectedChanpin.getChanpin_zujian().add(cz); // 添加到全局数据 Data.add(cz); }) .show(); } /** * 提交库存操作(进货/消耗) */ private void submitInventory() { // 获取数量 int quantity; try { quantity = Integer.parseInt(etQuantity.getText().toString()); } catch (NumberFormatException e) { Toast.makeText(requireContext(), "请输入有效数量", Toast.LENGTH_SHORT).show(); return; } // 获取操作类型 boolean isJinhuo = rgType.getCheckedRadioButtonId() == R.id.rb_jinhuo; // 权限检查:普通用户只能消耗 if (currentUser != null && currentUser.getRole() == 0 && isJinhuo) { Toast.makeText(requireContext(), "您只能执行生产操作", Toast.LENGTH_SHORT).show(); return; } // 创建库存记录 Jinhuo record = new Jinhuo(); record.setShuliang(isJinhuo ? quantity : -quantity); // 正数为进货,负数为消耗 record.setDate(new Date()); record.setUser(currentUser); // 设置关联关系 if (selectedBancai != null) { // 创建订单-板材关联(如果不存在) Dingdan_bancai db = createOrUpdateDingdanBancai(); record.setDingdan_bancai(db); } // 添加到全局数据 Data.add(record); Toast.makeText(requireContext(), "操作成功", Toast.LENGTH_SHORT).show(); resetForm(); } /** * 创建或更新订单-板材关联记录 */ private Dingdan_bancai createOrUpdateDingdanBancai() { // 检查是否已存在关联 Dingdan_bancai existing = findExistingDingdanBancai(); if (existing != null) { // 更新现有记录 return existing; } // 创建新关联 Dingdan_bancai db = new Dingdan_bancai(); if (selectedDingdan != null) db.setDingdan(selectedDingdan); if (selectedChanpin != null) db.setChanpin(selectedChanpin); if (selectedZujian != null) db.setZujian(selectedZujian); if (selectedBancai != null) db.setBancai(selectedBancai); // 添加到全局数据 Data.add(db); return db; } /** * 查找现有订单-板材关联记录 */ private Dingdan_bancai findExistingDingdanBancai() { for (Dingdan_bancai db : Data.Dingdan_bancais().getViewList()) { boolean matchDingdan = (selectedDingdan == null && db.getDingdan() == null) || (selectedDingdan != null && selectedDingdan.equals(db.getDingdan())); boolean matchChanpin = (selectedChanpin == null && db.getChanpin() == null) || (selectedChanpin != null && selectedChanpin.equals(db.getChanpin())); boolean matchZujian = (selectedZujian == null && db.getZujian() == null) || (selectedZujian != null && selectedZujian.equals(db.getZujian())); boolean matchBancai = selectedBancai != null && selectedBancai.equals(db.getBancai()); if (matchDingdan && matchChanpin && matchZujian && matchBancai) { return db; } } return null; } /** * 重置表单到初始状态 */ private void resetForm() { actvDingdan.setSelection(0); actvChanpin.setSelection(0); actvZujian.setSelection(0); actvBancai.setSelection(0); etQuantity.setText(""); rgType.check(R.id.rb_jinhuo); } @Override public void onResume() { super.onResume(); Data.addDataChangeListener(this); } @Override public void onPause() { super.onPause(); Data.removeDataChangeListener(this); } @Override public void onDataChanged(Class<?> entityClass, String operationType, Integer itemId) { // 6. 更新适配器数据 if (entityClass == Dingdan.class) { dingdanList = Data.dingdans().getViewList(); dingdanAdapter.updateList(dingdanList); // 尝试选中新添加的订单 if (operationType.equals("add")) { for (int i = 0; i < dingdanList.size(); i++) { if (Objects.equals(dingdanList.get(i).getId(), itemId)) { actvDingdan.setText(dingdanList.get(i).getNumber(), false); selectedDingdan = dingdanList.get(i); break; } } } } else if (entityClass == Chanpin.class) { chanpinList = Data.chanpins().getViewList(); updateChanpinSpinner(); } else if (entityClass == Zujian.class) { zujianList = Data.zujians().getViewList(); updateZujianSpinner(); } else if (entityClass == Bancai.class) { bancaiList = Data.bancais().getViewList(); bancaiAdapter.updateList(bancaiList); } } } ---------------------------------------------------AutoCompleteTextView中输入时现实的下拉框是订单编号,选择之后就显示为内存地址了
07-01
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值